Merge "[res] Correct, optimize adding shared lib assets" into main
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index a40adac..82ef3e6 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "10093150"
+    build_id: "11947186"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShimPriv.apk"
   }
@@ -8,7 +8,7 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "udc-dev"
+  git_branch: "main"
   transform: TRANSFORM_NONE
   transform_options {
   }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index 96444ba..7d0e5d7 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "10093150"
+    build_id: "11947186"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShim.apk"
   }
@@ -8,7 +8,7 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "udc-dev"
+  git_branch: "main"
   transform: TRANSFORM_NONE
   transform_options {
   }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 4d6f8ed..be32060 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "10093150"
+    build_id: "11947186"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShimPriv.apk"
   }
@@ -8,7 +8,7 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "udc-dev"
+  git_branch: "main"
   transform: TRANSFORM_NONE
   transform_options {
   }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index bfd6788..1a6448a 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "10093150"
+    build_id: "11947186"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShim.apk"
   }
@@ -8,7 +8,7 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "udc-dev"
+  git_branch: "main"
   transform: TRANSFORM_NONE
   transform_options {
   }
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2d05606..96d6f32 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -307,6 +307,11 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+cc_aconfig_library {
+    name: "android_security_flags_aconfig_c_lib",
+    aconfig_declarations: "android.security.flags-aconfig",
+}
+
 // UsageStats
 aconfig_declarations {
     name: "android.app.usage.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index af312bf..fb1fa3b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -115,7 +115,7 @@
         ":android.security.legacykeystore-java-source",
         ":android.security.maintenance-java-source",
         ":android.security.metrics-java-source",
-        ":android.system.keystore2-V3-java-source",
+        ":android.system.keystore2-V4-java-source",
         ":android.hardware.cas-V1-java-source",
         ":credstore_aidl",
         ":dumpstate_aidl",
@@ -404,6 +404,7 @@
         "android.hardware.common.fmq-V1-java",
         "bouncycastle-repackaged-unbundled",
         "com.android.sysprop.foldlockbehavior",
+        "com.android.sysprop.view",
         "framework-internal-utils",
         // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
         // in favor of an API stubs dependency in java_library "framework" below.
diff --git a/DREAM_MANAGER_OWNERS b/DREAM_MANAGER_OWNERS
deleted file mode 100644
index 48bde60..0000000
--- a/DREAM_MANAGER_OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-brycelee@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index e857175..fea2b7b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -30,7 +30,7 @@
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
 
 # This flag check hook runs only for "packages/SystemUI" subdirectory. If you want to include this check for other subdirectories, please modify flag_check.py.
-flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH}
+flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PROJECT}
 
 [Tool Paths]
-ktfmt = ${REPO_ROOT}/prebuilts/build-tools/common/framework/ktfmt.jar
+ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS
index 8ff3f9b..f4346b1 100644
--- a/apct-tests/perftests/OWNERS
+++ b/apct-tests/perftests/OWNERS
@@ -8,4 +8,3 @@
 shayba@google.com
 shombert@google.com
 timmurray@google.com
-wessam@google.com
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py b/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py
index 01abdb6..cfcb1d2 100755
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py
@@ -21,7 +21,7 @@
 To run use: python generate_java.py <destination_directory>
 
 And then to correct lint errors (from frameworks/base):
-../../tools/repohooks/tools/google-java-format.py --fix --sort-imports  --google-java-format-diff ../../external/google-java-format/scripts/google-java-format-diff.py
+../../tools/repohooks/tools/google-java-format.py --fix --google-java-format-diff ../../external/google-java-format/scripts/google-java-format-diff.py
 """
 
 
diff --git a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt
deleted file mode 100644
index 2af878e..0000000
--- a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt
+++ /dev/null
@@ -1,75 +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.permission
-
-import android.app.AppOpsManager
-import android.content.Context
-import androidx.benchmark.BenchmarkState
-import androidx.benchmark.junit4.BenchmarkRule
-import androidx.test.core.app.ApplicationProvider
-import androidx.test.filters.LargeTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-
-@LargeTest
-/**
- * Performance unit tests for app ops APIs.
- *
- * The APIs under test are used for checking permissions and tracking permission accesses and are
- * therefore invoked frequently by the system for all permission-protected data accesses, hence
- * these APIs should be monitored closely for performance.
- */
-class AppOpsPerfTest {
-    @get:Rule val mBenchmarkRule: BenchmarkRule = BenchmarkRule()
-    private lateinit var appOpsManager: AppOpsManager
-    private lateinit var opPackageName: String
-    private var opPackageUid: Int = 0
-
-    @Before
-    fun setUp() {
-        val context: Context = ApplicationProvider.getApplicationContext()
-        appOpsManager = context.getSystemService<AppOpsManager>(AppOpsManager::class.java)!!
-        opPackageName = context.getOpPackageName()
-        opPackageUid = context.getPackageManager().getPackageUid(opPackageName, 0)
-    }
-
-    @Test
-    fun testNoteOp() {
-        val state: BenchmarkState = mBenchmarkRule.getState()
-        while (state.keepRunning()) {
-            appOpsManager.noteOp(
-                    AppOpsManager.OPSTR_FINE_LOCATION,
-                    opPackageUid,
-                    opPackageName,
-                    null,
-                    null
-            )
-        }
-    }
-
-    @Test
-    fun testUnsafeCheckOp() {
-        val state: BenchmarkState = mBenchmarkRule.getState()
-        while (state.keepRunning()) {
-            appOpsManager.unsafeCheckOp(
-                    AppOpsManager.OPSTR_FINE_LOCATION,
-                    opPackageUid,
-                    opPackageName
-            )
-        }
-    }
-}
diff --git a/apct-tests/perftests/core/src/android/permission/OWNERS b/apct-tests/perftests/core/src/android/permission/OWNERS
deleted file mode 100644
index b4b2b9e..0000000
--- a/apct-tests/perftests/core/src/android/permission/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 137825
-
-include platform/frameworks/base:/core/java/android/permission/OWNERS
\ No newline at end of file
diff --git a/apct-tests/perftests/permission/src/android/perftests/permission/AppOpsPerfTest.kt b/apct-tests/perftests/permission/src/android/perftests/permission/AppOpsPerfTest.kt
new file mode 100644
index 0000000..b6a53bf
--- /dev/null
+++ b/apct-tests/perftests/permission/src/android/perftests/permission/AppOpsPerfTest.kt
@@ -0,0 +1,69 @@
+/*
+ * 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.perftests.permission
+
+import android.app.AppOpsManager
+import android.content.Context
+import android.perftests.utils.PerfStatusReporter
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class AppOpsPerfTest {
+    @get:Rule val perfStatusReporter = PerfStatusReporter()
+    private lateinit var appOpsManager: AppOpsManager
+    private lateinit var opPackageName: String
+    private var opPackageUid: Int = 0
+
+    @Before
+    fun setUp() {
+        val context: Context = ApplicationProvider.getApplicationContext()
+        appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
+        opPackageName = context.opPackageName
+        opPackageUid = context.getPackageManager().getPackageUid(opPackageName, 0)
+    }
+
+    @Test
+    fun testNoteOp() {
+        val state = perfStatusReporter.benchmarkState
+        while (state.keepRunning()) {
+            appOpsManager.noteOp(
+                AppOpsManager.OPSTR_FINE_LOCATION,
+                opPackageUid,
+                opPackageName,
+                null,
+                null
+            )
+        }
+    }
+
+    @Test
+    fun testUnsafeCheckOp() {
+        val state = perfStatusReporter.benchmarkState
+        while (state.keepRunning()) {
+            appOpsManager.unsafeCheckOp(
+                AppOpsManager.OPSTR_FINE_LOCATION,
+                opPackageUid,
+                opPackageName
+            )
+        }
+    }
+}
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 98767ee..3534624 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -74,7 +74,4 @@
         "libGLESv2",
         "libgui",
     ],
-    whole_static_libs: [
-        "libc++fs",
-    ],
 }
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 6e51f00..58763a7 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -313,7 +313,6 @@
         "libziparchive",
     ],
     static_libs: [
-        "libc++fs",
         "libidmap2_policies",
         "libidmap2_protos",
         "libidmap2daidl",
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index cffc078..cd1fb9a 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -35,7 +35,7 @@
     ],
     installable: false,
     flags: [
-        "-stubpackages com.android.uiautomator.core:com.android.uiautomator.testrunner",
+        "--stub-packages com.android.uiautomator.core:com.android.uiautomator.testrunner",
     ],
 
     check_api: {
diff --git a/core/api/current.txt b/core/api/current.txt
index c0bc6d9..5eeb299 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -5605,7 +5605,6 @@
     method @Deprecated public void onCancel(android.content.DialogInterface);
     method @Deprecated public android.app.Dialog onCreateDialog(android.os.Bundle);
     method @Deprecated public void onDismiss(android.content.DialogInterface);
-    method public android.view.LayoutInflater onGetLayoutInflater(android.os.Bundle);
     method @Deprecated public void setCancelable(boolean);
     method @Deprecated public void setShowsDialog(boolean);
     method @Deprecated public void setStyle(int, int);
@@ -9801,6 +9800,7 @@
     method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
     method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
     method @FlaggedApi("android.companion.perm_sync_user_consent") public boolean isPermissionTransferUserConsented(int);
+    method @FlaggedApi("android.companion.unpair_associated_device") @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond(int);
     method public void requestNotificationAccess(android.content.ComponentName);
     method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
@@ -15974,6 +15974,7 @@
     enum_constant public static final android.graphics.ColorSpace.Named LINEAR_EXTENDED_SRGB;
     enum_constant public static final android.graphics.ColorSpace.Named LINEAR_SRGB;
     enum_constant public static final android.graphics.ColorSpace.Named NTSC_1953;
+    enum_constant @FlaggedApi("com.android.graphics.flags.ok_lab_colorspace") public static final android.graphics.ColorSpace.Named OK_LAB;
     enum_constant public static final android.graphics.ColorSpace.Named PRO_PHOTO_RGB;
     enum_constant public static final android.graphics.ColorSpace.Named SMPTE_C;
     enum_constant public static final android.graphics.ColorSpace.Named SRGB;
@@ -19910,7 +19911,6 @@
   }
 
   @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureRequest {
-    ctor public ExtensionCaptureRequest();
     field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> EFV_AUTO_ZOOM;
     field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR;
     field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR;
@@ -19923,7 +19923,6 @@
   }
 
   @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureResult {
-    ctor public ExtensionCaptureResult();
     field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> EFV_AUTO_ZOOM;
     field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION;
     field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR;
@@ -34055,6 +34054,7 @@
     field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
     field public static final String DISALLOW_CAMERA_TOGGLE = "disallow_camera_toggle";
     field public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g";
+    field @FlaggedApi("android.nfc.enable_nfc_user_restriction") public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO = "no_change_near_field_communication_radio";
     field public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state";
     field public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
     field public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness";
@@ -47133,6 +47133,8 @@
     method public int describeContents();
     method public int getAttributeFlags();
     method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
+    method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public boolean getFlagRegistrationTypeEmergency();
+    method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public boolean getFlagVirtualRegistrationForEmergencyCall();
     method @Nullable public android.telephony.ims.SipDetails getSipDetails();
     method public int getTransportType();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -55307,7 +55309,7 @@
     field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6
     field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
     field public static final int TYPE_SYSTEM = 3; // 0x3
-    field @FlaggedApi("android.view.accessibility.add_type_window_control") public static final int TYPE_WINDOW_CONTROL = 7; // 0x7
+    field @FlaggedApi("android.view.accessibility.enable_type_window_control") public static final int TYPE_WINDOW_CONTROL = 7; // 0x7
   }
 
   public class CaptioningManager {
diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt
index 1b0da05..e6a7ca5 100644
--- a/core/api/lint-baseline.txt
+++ b/core/api/lint-baseline.txt
@@ -245,14 +245,6 @@
     Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior
 
 
-CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL:
-    All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL
-CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED:
-    All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED
-CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF:
-    All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF
-
-
 DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle):
     Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match
 DeprecationMismatch: android.app.Activity#enterPictureInPictureMode():
@@ -1095,14 +1087,6 @@
     Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission
 
 
-StaticUtils: ExtensionCaptureRequest:
-    Fully-static utility classes must not have constructor
-StaticUtils: android.hardware.camera2.ExtensionCaptureRequest:
-    Fully-static utility classes must not have constructor
-StaticUtils: android.hardware.camera2.ExtensionCaptureResult:
-    Fully-static utility classes must not have constructor
-
-
 Todo: android.hardware.camera2.params.StreamConfigurationMap:
     Documentation mentions 'TODO'
 Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor):
@@ -1462,14 +1446,6 @@
 UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int):
     New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int)
 
-UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest:
-    New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureRequest
-UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest#ExtensionCaptureRequest():
-    New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureRequest()
-UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult:
-    New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureResult
-UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult#ExtensionCaptureResult():
-    New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureResult()
 UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR:
     New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR
 UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER:
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 14ae3f5..c2f960f 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -46,6 +46,7 @@
     field public static final String REMAP_MODIFIER_KEYS = "android.permission.REMAP_MODIFIER_KEYS";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String REQUEST_UNIQUE_ID_ATTESTATION = "android.permission.REQUEST_UNIQUE_ID_ATTESTATION";
+    field public static final String RESERVED_FOR_TESTING_SIGNATURE = "android.permission.RESERVED_FOR_TESTING_SIGNATURE";
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
     field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL";
     field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
@@ -281,6 +282,7 @@
     field public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
     field public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE = "android:activity_recognition_source";
     field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
+    field @FlaggedApi("android.service.notification.redact_sensitive_notifications_from_untrusted_listeners") public static final String OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS = "android:receive_sensitive_notifications";
     field public static final String OPSTR_RECORD_AUDIO_HOTWORD = "android:record_audio_hotword";
     field public static final String OPSTR_RESERVED_FOR_TESTING = "android:reserved_for_testing";
     field public static final String OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android:use_icc_auth_with_device_identifier";
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 6cc71e5..b4a3abc 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1973,6 +1973,8 @@
 
 UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
+UnflaggedApi: android.Manifest.permission#RESERVED_FOR_TESTING_SIGNATURE:
+    New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESERVED_FOR_TESTING_SIGNATURE
 UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX
 UnflaggedApi: android.Manifest.permission#USE_REMOTE_AUTH:
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 65628d3..fd9600c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,7 +16,6 @@
 
 package android.accessibilityservice;
 
-import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION;
 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 
@@ -70,8 +69,6 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.inputmethod.EditorInfo;
 
-import androidx.annotation.GuardedBy;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
@@ -643,8 +640,6 @@
         /** The detected gesture information for different displays */
         boolean onGesture(AccessibilityGestureEvent gestureInfo);
         boolean onKeyEvent(KeyEvent event);
-        /** Magnification SystemUI connection changed callbacks */
-        void onMagnificationSystemUIConnectionChanged(boolean connected);
         /** Magnification changed callbacks for different displays */
         void onMagnificationChanged(int displayId, @NonNull Region region,
                 MagnificationConfig config);
@@ -795,6 +790,7 @@
     public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP =
             "screenshot_timestamp";
 
+
     /**
      * Annotations for result codes of attaching accessibility overlays.
      *
@@ -841,13 +837,6 @@
 
     private WindowManager mWindowManager;
 
-    @GuardedBy("mLock")
-    private boolean mServiceConnected;
-    @GuardedBy("mLock")
-    private boolean mMagnificationSystemUIConnected;
-    @GuardedBy("mLock")
-    private boolean mServiceConnectedNotified;
-
     /** List of magnification controllers, mapping from displayId -> MagnificationController. */
     private final SparseArray<MagnificationController> mMagnificationControllers =
             new SparseArray<>(0);
@@ -897,14 +886,11 @@
             for (int i = 0; i < mMagnificationControllers.size(); i++) {
                 mMagnificationControllers.valueAt(i).onServiceConnectedLocked();
             }
-            checkIsMagnificationSystemUIConnectedAlready();
             final AccessibilityServiceInfo info = getServiceInfo();
             if (info != null) {
                 updateInputMethod(info);
                 mMotionEventSources = info.getMotionEventSources();
             }
-            mServiceConnected = true;
-            mServiceConnectedNotified = false;
         }
         if (mSoftKeyboardController != null) {
             mSoftKeyboardController.onServiceConnected();
@@ -912,57 +898,7 @@
 
         // The client gets to handle service connection last, after we've set
         // up any state upon which their code may rely.
-        if (android.view.accessibility.Flags
-                .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) {
-            notifyOnServiceConnectedIfReady();
-        } else {
-            onServiceConnected();
-        }
-    }
-
-    private void notifyOnServiceConnectedIfReady() {
-        synchronized (mLock) {
-            if (mServiceConnectedNotified) {
-                return;
-            }
-            boolean canControlMagnification;
-            final AccessibilityServiceInfo info = getServiceInfo();
-            if (info != null) {
-                int flagMask = CAPABILITY_CAN_CONTROL_MAGNIFICATION;
-                canControlMagnification = (info.getCapabilities() & flagMask) == flagMask;
-            } else {
-                canControlMagnification = false;
-            }
-            boolean ready = canControlMagnification
-                    ? (mServiceConnected && mMagnificationSystemUIConnected)
-                    : mServiceConnected;
-            if (ready) {
-                getMainExecutor().execute(() -> onServiceConnected());
-                mServiceConnectedNotified = true;
-            }
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void checkIsMagnificationSystemUIConnectedAlready() {
-        if (!android.view.accessibility.Flags
-                .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) {
-            return;
-        }
-        if (mMagnificationSystemUIConnected) {
-            return;
-        }
-        final IAccessibilityServiceConnection connection =
-                AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
-        if (connection != null) {
-            try {
-                boolean connected = connection.isMagnificationSystemUIConnected();
-                mMagnificationSystemUIConnected = connected;
-            } catch (RemoteException re) {
-                Log.w(LOG_TAG, "Failed to check magnification system ui connection", re);
-                re.rethrowFromSystemServer();
-            }
-        }
+        onServiceConnected();
     }
 
     private void updateInputMethod(AccessibilityServiceInfo info) {
@@ -1424,22 +1360,6 @@
         }
     }
 
-    private void onMagnificationSystemUIConnectionChanged(boolean connected) {
-        if (!android.view.accessibility.Flags
-                .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) {
-            return;
-        }
-
-        synchronized (mLock) {
-            boolean changed = (mMagnificationSystemUIConnected != connected);
-            mMagnificationSystemUIConnected = connected;
-
-            if (changed) {
-                notifyOnServiceConnectedIfReady();
-            }
-        }
-    }
-
     private void onMagnificationChanged(int displayId, @NonNull Region region,
             MagnificationConfig config) {
         MagnificationController controller;
@@ -2903,11 +2823,6 @@
             }
 
             @Override
-            public void onMagnificationSystemUIConnectionChanged(boolean connected) {
-                AccessibilityService.this.onMagnificationSystemUIConnectionChanged(connected);
-            }
-
-            @Override
             public void onMagnificationChanged(int displayId, @NonNull Region region,
                     MagnificationConfig config) {
                 AccessibilityService.this.onMagnificationChanged(displayId, region, config);
@@ -3117,16 +3032,6 @@
             });
         }
 
-        @Override
-        public void onMagnificationSystemUIConnectionChanged(boolean connected) {
-            mExecutor.execute(() -> {
-                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                    mCallback.onMagnificationSystemUIConnectionChanged(connected);
-                }
-                return;
-            });
-        }
-
         /** Magnification changed callbacks for different displays */
         public void onMagnificationChanged(int displayId, @NonNull Region region,
                 MagnificationConfig config) {
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index f1479ef..3bc61e5 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -48,8 +48,6 @@
 
     void onKeyEvent(in KeyEvent event, int sequence);
 
-    void onMagnificationSystemUIConnectionChanged(boolean connected);
-
     void onMagnificationChanged(int displayId, in Region region, in MagnificationConfig config);
 
     void onMotionEvent(in MotionEvent event);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 149e719..713d8e5 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -130,9 +130,6 @@
     void setMagnificationCallbackEnabled(int displayId, boolean enabled);
 
     @RequiresNoPermission
-    boolean isMagnificationSystemUIConnected();
-
-    @RequiresNoPermission
     boolean setSoftKeyboardShowMode(int showMode);
 
     @RequiresNoPermission
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 79e2bd4..c8e1e4d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1150,6 +1150,11 @@
      *
      * <p>To keep the Intent instance for future use, call {@link #setIntent(Intent)}, and use
      * this method to retrieve it.
+     *
+     * <p>Note that in {@link #onNewIntent}, this method will return the original Intent. You can
+     * use {@link #setIntent(Intent)} to update it to the new Intent.
+     *
+     * @return {@link Intent} instance that started this activity, or that was kept for future use
      */
     public Intent getIntent() {
         return mIntent;
@@ -1170,9 +1175,14 @@
     }
 
     /**
-     * Returns the ComponentCaller instance of the app that launched this activity with the intent
-     * from {@link #getIntent()}. To keep the value of the ComponentCaller instance for new intents,
-     * call {@link #setIntent(Intent, ComponentCaller)} instead of {@link #setIntent(Intent)}.
+     * Returns the ComponentCaller instance of the app that started this activity.
+     *
+     * <p>To keep the ComponentCaller instance for future use, call
+     * {@link #setIntent(Intent, ComponentCaller)}, and use this method to retrieve it.
+     *
+     * <p>Note that in {@link #onNewIntent}, this method will return the original ComponentCaller.
+     * You can use {@link #setIntent(Intent, ComponentCaller)} to update it to the new
+     * ComponentCaller.
      *
      * @return {@link ComponentCaller} instance corresponding to the intent from
      *         {@link #getIntent()}, or {@code null} if the activity was not launched with that
@@ -7156,8 +7166,8 @@
     /**
      * Returns the ComponentCaller instance of the app that initially launched this activity.
      *
-     * <p>Note that calls to {@link #onNewIntent} have no effect on the returned value of this
-     * method.
+     * <p>Note that calls to {@link #onNewIntent} and {@link #setIntent} have no effect on the
+     * returned value of this method.
      *
      * @return {@link ComponentCaller} instance
      * @see ComponentCaller
@@ -7555,6 +7565,12 @@
      * the activity to be restarted). Otherwise, this will be used the next
      * time the activity is visible.
      *
+     * <aside class="note"><b>Note:</b> Device manufacturers can configure devices to override
+     *    (ignore) calls to this method to improve the layout of orientation-restricted apps. See
+     *    <a href="{@docRoot}guide/practices/device-compatibility-mode">
+     *      Device compatibility mode</a>.
+     * </aside>
+     *
      * @param requestedOrientation An orientation constant as used in
      * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
      */
@@ -9291,11 +9307,11 @@
         if (DEBUG_LIFECYCLE) Slog.v(TAG,
                 "dispatchMultiWindowModeChanged " + this + ": " + isInMultiWindowMode
                         + " " + newConfig);
+        mIsInMultiWindowMode = isInMultiWindowMode;
         mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig);
         if (mWindow != null) {
             mWindow.onMultiWindowModeChanged();
         }
-        mIsInMultiWindowMode = isInMultiWindowMode;
         onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
     }
 
@@ -9304,11 +9320,11 @@
         if (DEBUG_LIFECYCLE) Slog.v(TAG,
                 "dispatchPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode
                         + " " + newConfig);
+        mIsInPictureInPictureMode = isInPictureInPictureMode;
         mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
         if (mWindow != null) {
             mWindow.onPictureInPictureModeChanged(isInPictureInPictureMode);
         }
-        mIsInPictureInPictureMode = isInPictureInPictureMode;
         onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);
     }
 
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index fa8fe3b..74e9583 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4222,6 +4222,10 @@
      * Return a list of {@link ApplicationStartInfo} records containing the information about the
      * most recent app startups.
      *
+     * Records accessed using this path might include "incomplete" records such as in-progress app
+     * starts. Accessing in-progress starts using this method lets you access start information
+     * early to better optimize your startup path.
+     *
      * <p class="note"> Note: System stores this historical information in a ring buffer and only
      * the most recent records will be returned. </p>
      *
@@ -4249,6 +4253,9 @@
      * Return a list of {@link ApplicationStartInfo} records containing the information about the
      * most recent app startups.
      *
+     * Records accessed using this path might include "incomplete" records such as in-progress app
+     * starts.
+     *
      * <p class="note"> Note: System stores this historical information in a ring buffer and only
      * the most recent records will be returned. </p>
      *
@@ -4294,17 +4301,19 @@
     }
 
     /**
-     * Adds a callback to be notified when the {@link ApplicationStartInfo} records of this startup
-     * are complete.
+     * Adds a callback that is notified when the {@link ApplicationStartInfo} record of this startup
+     * is complete. The startup is considered complete when the first frame is drawn.
      *
-     * <p class="note"> Note: callback will be removed automatically after being triggered.</p>
+     * The callback doesn't wait for {@link Activity#reportFullyDrawn} to occur. Retrieve a copy
+     * of {@link ApplicationStartInfo} after {@link Activity#reportFullyDrawn} is called (using this
+     * callback or {@link getHistoricalProcessStartReasons}) if you need the
+     * {@link ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN} timestamp.
      *
-     * <p class="note"> Note: callback will not wait for {@link Activity#reportFullyDrawn} to occur.
-     * Timestamp for fully drawn may be added after callback occurs. Set callback after invoking
-     * {@link Activity#reportFullyDrawn} if timestamp for fully drawn is required.</p>
+     * If the current start record has already been completed (that is, the process is not currently
+     * starting), the callback will be invoked immediately on the specified executor with the
+     * previously completed {@link ApplicationStartInfo} record.
      *
-     * <p class="note"> Note: if start records have already been retrieved, the callback will be
-     * invoked immediately on the specified executor with the previously resolved AppStartInfo.</p>
+     * Callback will be called at most once and removed automatically after being triggered.
      *
      * <p class="note"> Note: callback is asynchronous and should be made from a background thread.
      * </p>
@@ -4394,8 +4403,8 @@
      * Adds an optional developer supplied timestamp to the calling apps most recent
      * {@link ApplicationStartInfo}. This is in addition to system recorded timestamps.
      *
-     * <p class="note"> Note: timestamps added after {@link Activity#reportFullyDrawn} is called
-     * will be discarded.</p>
+     * <p class="note"> Note: any timestamps added after {@link Activity#reportFullyDrawn} is called
+     * are discarded.</p>
      *
      * <p class="note"> Note: will overwrite existing timestamp if called with same key.</p>
      *
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9785252..83c3bf6 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -477,6 +477,11 @@
      */
     public static final int OOM_ADJ_REASON_COMPONENT_DISABLED = 22;
 
+    /**
+     * Oom Adj Reason: Follow up update for time sensitive state evaluations.
+     */
+    public static final int OOM_ADJ_REASON_FOLLOW_UP = 23;
+
     @IntDef(prefix = {"OOM_ADJ_REASON_"}, value = {
         OOM_ADJ_REASON_NONE,
         OOM_ADJ_REASON_ACTIVITY,
@@ -501,6 +506,7 @@
         OOM_ADJ_REASON_EXECUTING_SERVICE,
         OOM_ADJ_REASON_RESTRICTION_CHANGE,
         OOM_ADJ_REASON_COMPONENT_DISABLED,
+        OOM_ADJ_REASON_FOLLOW_UP,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface OomAdjReason {}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 76c1ed6..b384326 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7500,7 +7500,7 @@
                     + data.instrumentationName + ": " + e.toString(), e);
             }
             try {
-                timestampApplicationOnCreateNs = SystemClock.elapsedRealtimeNanos();
+                timestampApplicationOnCreateNs = SystemClock.uptimeNanos();
                 mInstrumentation.callApplicationOnCreate(app);
             } catch (Exception e) {
                 timestampApplicationOnCreateNs = 0;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 6865f9c..2313fa2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -20,6 +20,7 @@
 import static android.location.flags.Flags.FLAG_LOCATION_BYPASS;
 import static android.media.audio.Flags.roForegroundAudioControl;
 import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER;
+import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS;
 import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED;
 import static android.view.contentprotection.flags.Flags.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED;
 
@@ -1597,9 +1598,19 @@
      */
     public static final int OP_EMERGENCY_LOCATION = AppProtoEnums.APP_OP_EMERGENCY_LOCATION;
 
+    /**
+     * Allows apps with a NotificationListenerService to receive notifications with sensitive
+     * information
+     * <p>Apps with a NotificationListenerService without this permission will not be able
+     * to view certain types of sensitive information contained in notifications
+     * @hide
+     */
+    public static final int OP_RECEIVE_SENSITIVE_NOTIFICATIONS =
+            AppProtoEnums.APP_OP_RECEIVE_SENSITIVE_NOTIFICATIONS;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 148;
+    public static final int _NUM_OP = 149;
 
     /**
      * All app ops represented as strings.
@@ -1751,6 +1762,7 @@
             OPSTR_ARCHIVE_ICON_OVERLAY,
             OPSTR_UNARCHIVAL_CONFIRMATION,
             OPSTR_EMERGENCY_LOCATION,
+            OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS,
     })
     public @interface AppOpString {}
 
@@ -2476,6 +2488,18 @@
     @FlaggedApi(FLAG_LOCATION_BYPASS)
     public static final String OPSTR_EMERGENCY_LOCATION = "android:emergency_location";
 
+    /**
+     * Allows apps with a NotificationListenerService to receive notifications with sensitive
+     * information
+     * <p>Apps with a NotificationListenerService without this permission will not be able
+     * to view certain types of sensitive information contained in notifications
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS)
+    public static final String OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS =
+            "android:receive_sensitive_notifications";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -3055,6 +3079,9 @@
                 // even though this has a permission associated, this op is only used for tracking,
                 // and the client is responsible for checking the LOCATION_BYPASS permission.
                 .setPermission(Manifest.permission.LOCATION_BYPASS).build(),
+        new AppOpInfo.Builder(OP_RECEIVE_SENSITIVE_NOTIFICATIONS,
+                OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS, "RECEIVE_SENSITIVE_NOTIFICATIONS")
+                .setDefaultMode(MODE_IGNORED).build(),
     };
 
     // The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index f8a8f5d..b21defb 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -172,11 +172,9 @@
          * @param virtualDeviceId the device for which to finish the op
          * @param superImpl
          */
-        default void finishOperation(IBinder clientId, int code, int uid, String packageName,
+        void finishOperation(IBinder clientId, int code, int uid, String packageName,
                 String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
-                        Integer, String, String, Integer> superImpl) {
-            superImpl.accept(clientId, code, uid, packageName, attributionTag, virtualDeviceId);
-        }
+                        Integer, String, String, Integer> superImpl);
 
         /**
          * Allows overriding finish proxy op.
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index 3715c6e..0ff5514 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -60,6 +60,16 @@
  * start times, throttling, and other useful diagnostic data can be obtained from
  * {@link ApplicationStartInfo} records.
  * </p>
+ *
+ * <p>
+*  ApplicationStartInfo objects can be retrieved via:
+*  - {@link ActivityManager#getHistoricalProcessStartReasons}, which can be called during or after
+ *      a application's startup. Using this method, an app can retrieve information about an
+ *      in-progress app start.
+*  - {@link ActivityManager#addApplicationStartInfoCompletionListener}, which returns an
+ *      ApplicationStartInfo object via a callback when the startup is complete, or immediately
+ *      if requested after the startup is complete.
+ * </p>
  */
 @FlaggedApi(Flags.FLAG_APP_START_INFO)
 public final class ApplicationStartInfo implements Parcelable {
@@ -416,11 +426,34 @@
 
     /**
      * @see #getStartIntent
+     *
+     * <p class="note"> Note: This method will clone the provided intent and ensure that the cloned
+     * intent doesn't contain any large objects like bitmaps in its extras by stripping it in the
+     * least aggressive acceptable way for the individual intent.</p>
+     *
      * @hide
      */
     public void setIntent(Intent startIntent) {
         if (startIntent != null) {
-            mStartIntent = startIntent.maybeStripForHistory();
+            if (startIntent.canStripForHistory()) {
+                // If maybeStripForHistory will return a lightened version, do that.
+                mStartIntent = startIntent.maybeStripForHistory();
+            } else if (startIntent.getExtras() != null) {
+                // If maybeStripForHistory would not return a lightened version and extras is
+                // non-null then extras contains un-parcelled data. Use cloneFilter to strip data
+                // more aggressively.
+                mStartIntent = startIntent.cloneFilter();
+            } else {
+                // Finally, if maybeStripForHistory would not return a lightened version and extras
+                // is null then do a regular clone so we don't leak the intent.
+                mStartIntent = new Intent(startIntent);
+            }
+
+            // If the newly cloned intent has an original intent, clear that as we don't need it and
+            // can't guarantee it doesn't need to be stripped as well.
+            if (mStartIntent.getOriginalIntent() != null) {
+                mStartIntent.setOriginalIntent(null);
+            }
         }
     }
 
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 15b13dc..ffb920b 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -757,6 +757,15 @@
     void addStartInfoTimestamp(int key, long timestampNs, int userId);
 
     /**
+    * Reports view related timestamps to be added to the calling apps most
+    * recent {@link ApplicationStartInfo}.
+    *
+    * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start
+    * @param framePresentedTimeNs        Clock monotonic time in nanoseconds of frame presented
+    */
+    oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs);
+
+    /**
      * Return a list of {@link ApplicationExitInfo} records.
      *
      * <p class="note"> Note: System stores these historical information in a ring buffer, older
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index cf06416..e2bee64 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -42,6 +42,7 @@
 import android.service.notification.ZenPolicy;
 import android.app.AutomaticZenRule;
 import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenDeviceEffects;
 
 /** {@hide} */
 interface INotificationManager
@@ -121,6 +122,7 @@
     boolean onlyHasDefaultChannel(String pkg, int uid);
     boolean areChannelsBypassingDnd();
     ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid);
+    List<String> getPackagesBypassingDnd(int userId, boolean includeConversationChannels);
     boolean isPackagePaused(String pkg);
     void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
     boolean isPermissionFixed(String pkg, int userId);
@@ -227,6 +229,7 @@
     int getRuleInstanceCount(in ComponentName owner);
     int getAutomaticZenRuleState(String id);
     void setAutomaticZenRuleState(String id, in Condition condition);
+    void setManualZenRuleDeviceEffects(in ZenDeviceEffects effects);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index fc3bb02..a1c4267 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2239,9 +2239,6 @@
 
         private void visitUris(@NonNull Consumer<Uri> visitor) {
             visitIconUri(visitor, getIcon());
-            if (actionIntent != null) {
-                actionIntent.visitUris(visitor);
-            }
         }
 
         @Override
@@ -2957,21 +2954,6 @@
             }
         }
 
-        // allPendingIntents should contain all associated intents after parcelling, but it may also
-        // contain intents added by the app to extras for their own purposes. We only care about
-        // checking the intents known and used by system_server, to avoid the confused deputy issue.
-        List<PendingIntent> pendingIntents = Arrays.asList(contentIntent, deleteIntent,
-                fullScreenIntent);
-        for (PendingIntent intent : pendingIntents) {
-            if (intent != null) {
-                intent.visitUris(visitor);
-            }
-        }
-
-        if (mBubbleMetadata != null) {
-            mBubbleMetadata.visitUris(visitor);
-        }
-
         if (extras != null) {
             visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG, Icon.class));
             visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON, Icon.class));
@@ -3043,28 +3025,15 @@
                 callPerson.visitUris(visitor);
             }
             visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class));
+        }
 
-            // Extras for MediaStyle.
-            PendingIntent deviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT,
-                    PendingIntent.class);
-            if (deviceIntent != null) {
-                deviceIntent.visitUris(visitor);
-            }
+        if (mBubbleMetadata != null) {
+            visitIconUri(visitor, mBubbleMetadata.getIcon());
+        }
 
-            if (extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) {
-                WearableExtender extender = new WearableExtender(this);
-                extender.visitUris(visitor);
-            }
-
-            if (extras.containsKey(TvExtender.EXTRA_TV_EXTENDER)) {
-                TvExtender extender = new TvExtender(this);
-                extender.visitUris(visitor);
-            }
-
-            if (extras.containsKey(CarExtender.EXTRA_CAR_EXTENDER)) {
-                CarExtender extender = new CarExtender(this);
-                extender.visitUris(visitor);
-            }
+        if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) {
+            WearableExtender extender = new WearableExtender(this);
+            extender.visitUris(visitor);
         }
     }
 
@@ -6253,6 +6222,7 @@
                 if (appIconRes != 0) {
                     mN.mAppIcon = Icon.createWithResource(mContext, appIconRes);
                     contentView.setImageViewIcon(R.id.icon, mN.mAppIcon);
+                    contentView.setBoolean(R.id.icon, "setShouldShowAppIcon", true);
                     usingAppIcon = true;
                 } else {
                     Log.w(TAG, "bindSmallIcon: could not get the app icon");
@@ -11459,16 +11429,6 @@
             }
         }
 
-        private void visitUris(@NonNull Consumer<Uri> visitor) {
-            visitIconUri(visitor, getIcon());
-            if (mPendingIntent != null) {
-                mPendingIntent.visitUris(visitor);
-            }
-            if (mDeleteIntent != null) {
-                mDeleteIntent.visitUris(visitor);
-            }
-        }
-
         /**
          * Builder to construct a {@link BubbleMetadata} object.
          */
@@ -12667,9 +12627,6 @@
         }
 
         private void visitUris(@NonNull Consumer<Uri> visitor) {
-            if (mDisplayIntent != null) {
-                mDisplayIntent.visitUris(visitor);
-            }
             for (Action action : mActions) {
                 action.visitUris(visitor);
             }
@@ -12829,12 +12786,6 @@
             return mUnreadConversation;
         }
 
-        private void visitUris(@NonNull Consumer<Uri> visitor) {
-            if (mUnreadConversation != null) {
-                mUnreadConversation.visitUris(visitor);
-            }
-        }
-
         /**
          * A class which holds the unread messages from a conversation.
          */
@@ -12986,16 +12937,7 @@
                         onRead,
                         participants, b.getLong(KEY_TIMESTAMP));
             }
-
-            private void visitUris(@NonNull Consumer<Uri> visitor) {
-                if (mReadPendingIntent != null) {
-                    mReadPendingIntent.visitUris(visitor);
-                }
-                if (mReplyPendingIntent != null) {
-                    mReplyPendingIntent.visitUris(visitor);
-                }
-            }
-        }
+        };
 
         /**
          * Builder class for {@link CarExtender.UnreadConversation} objects.
@@ -13318,15 +13260,6 @@
         public boolean isSuppressShowOverApps() {
             return mSuppressShowOverApps;
         }
-
-        private void visitUris(@NonNull Consumer<Uri> visitor) {
-            if (mContentIntent != null) {
-                mContentIntent.visitUris(visitor);
-            }
-            if (mDeleteIntent != null) {
-                mDeleteIntent.visitUris(visitor);
-            }
-        }
     }
 
     /**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index e4310c1..bd80dc1 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -57,6 +57,7 @@
 import android.service.notification.Adjustment;
 import android.service.notification.Condition;
 import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenPolicy;
 import android.util.Log;
@@ -1828,6 +1829,18 @@
             throw e.rethrowFromSystemServer();
         }
     }
+    /**
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_UI)
+    public void setManualZenRuleDeviceEffects(@NonNull ZenDeviceEffects effects) {
+        INotificationManager service = getService();
+        try {
+            service.setManualZenRuleDeviceEffects(effects);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     /**
      * For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above, the
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 2e38c06..0fad979 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -61,7 +61,7 @@
 per-file ComponentCaller.java = file:COMPONENT_CALLER_OWNERS
 
 # DreamManager
-per-file DreamManager.java = file:/DREAM_MANAGER_OWNERS
+per-file DreamManager.java = file:/core/java/android/service/dreams/OWNERS
 
 # GrammaticalInflectionManager
 per-file *GrammaticalInflection* = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 1ac08ac..3714e5d 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -44,8 +44,6 @@
 import android.content.pm.PackageManager.ResolveInfoFlagsBits;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -71,7 +69,6 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
-import java.util.function.Consumer;
 
 /**
  * A description of an Intent and target action to perform with it.  Instances
@@ -1471,21 +1468,6 @@
         return sb.toString();
     }
 
-    /**
-     * See {@link Intent#visitUris(Consumer)}.
-     *
-     * @hide
-     */
-    public void visitUris(@NonNull Consumer<Uri> visitor) {
-        if (android.app.Flags.visitRiskyUris()) {
-            Intent intent = Binder.withCleanCallingIdentity(this::getIntent);
-
-            if (intent != null) {
-                intent.visitUris(visitor);
-            }
-        }
-    }
-
     /** @hide */
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java
index 041866001..96f6f4e 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -189,7 +189,7 @@
      */
     public void visitUris(@NonNull Consumer<Uri> visitor) {
         visitor.accept(getIconUri());
-        if (Flags.visitRiskyUris()) {
+        if (Flags.visitPersonUri()) {
             if (mUri != null && !mUri.isEmpty()) {
                 visitor.accept(Uri.parse(mUri));
             }
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 273a79e..348d4d8f 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1969,11 +1969,6 @@
                 }
 
                 @Override
-                public void onMagnificationSystemUIConnectionChanged(boolean connected) {
-                    /* do nothing */
-                }
-
-                @Override
                 public void onMagnificationChanged(int displayId, @NonNull Region region,
                         MagnificationConfig config) {
                     /* do nothing */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 67752f2..fb0ce0d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13725,6 +13725,13 @@
      * {@link #EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT} in the provisioning parameters.
      * In that case the device owner's control will be limited to denying these permissions.
      * <p>
+     * When sensor-related permissions aren't grantable due to the above cases, calling this method
+     * to grant these permissions will silently fail, if device admins are built with
+     * {@code targetSdkVersion} &lt; {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}. If
+     * they are built with {@code targetSdkVersion} &gt;=
+     * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, this method will throw a
+     * {@link SecurityException}.
+     * <p>
      * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, control over
      * the following permissions are restricted for managed profile owners:
      * <ul>
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 7d5806a..8227112 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -383,3 +383,13 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+    name: "management_mode_policy_metrics"
+    namespace: "enterprise"
+    description: "Enabling management mode and password complexity policy metrics collection"
+    bug: "293091314"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index e5316bc0..a488689 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -34,12 +34,22 @@
     }
 
     /**
+     * Create an AssistContent with extras initialized.
+     *
      * @hide
+     */
+    public AssistContent(@android.annotation.NonNull Bundle extras) {
+        mExtras = extras;
+    }
+
+    /**
      * Called by {@link android.app.ActivityThread} to set the default Intent based on
      * {@link android.app.Activity#getIntent Activity.getIntent}.
      *
      * <p>Automatically populates {@link #mUri} if that Intent is an {@link Intent#ACTION_VIEW}
      * of a web (http or https scheme) URI.</p>
+     *
+     * @hide
      */
     public void setDefaultIntent(Intent intent) {
         mIntent = intent;
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
index 3385b2b..3b0c867 100644
--- a/core/java/android/app/contextualsearch/flags.aconfig
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -7,3 +7,9 @@
   description: "Flag to enable the service"
   bug: "309689654"
 }
+flag {
+  name: "enable_token_refresh"
+  namespace: "machine_learning"
+  description: "Flag to refresh the token to the callback"
+  bug: "309689654"
+}
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 2d78317..f751a23 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -46,10 +46,13 @@
 }
 
 flag {
-  name: "visit_risky_uris"
+  name: "visit_person_uri"
   namespace: "systemui"
-  description: "Guards the security fix that ensures all URIs in intents and Person.java are valid"
+  description: "Guards the security fix that ensures all URIs Person.java are valid"
   bug: "281044385"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
 
 # vvv Prototypes for using app icons in notifications vvv
@@ -199,3 +202,11 @@
   description: "redacts notifications on the lockscreen if they have the 'sensitiveContent' flag"
   bug: "343631648"
 }
+
+flag {
+  name: "api_rich_ongoing"
+  is_exported: true
+  namespace: "systemui"
+  description: "Guards new android.app.richongoingnotification api"
+  bug: "337261753"
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
index 470b1ec..1977a39 100644
--- a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
+++ b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
@@ -24,6 +24,8 @@
  import android.os.Bundle;
  import android.app.ondeviceintelligence.Feature;
  import android.app.ondeviceintelligence.FeatureDetails;
+ import android.app.ondeviceintelligence.InferenceInfo;
+ import java.util.List;
  import android.app.ondeviceintelligence.IDownloadCallback;
  import android.app.ondeviceintelligence.IListFeaturesCallback;
  import android.app.ondeviceintelligence.IFeatureCallback;
@@ -72,4 +74,6 @@
                     in IStreamingResponseCallback streamingCallback) = 8;
 
       String getRemoteServicePackageName() = 9;
+
+      List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) = 10;
  }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl
similarity index 69%
copy from packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
copy to core/java/android/app/ondeviceintelligence/InferenceInfo.aidl
index 23dbc26..6d70fc4 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
+++ b/core/java/android/app/ondeviceintelligence/InferenceInfo.aidl
@@ -5,7 +5,7 @@
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recordissue
+package android.app.ondeviceintelligence;
 
-import com.android.traceur.TraceUtils.PresetTraceType
-
-data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
+/**
+  * @hide
+  */
+parcelable InferenceInfo;
diff --git a/core/java/android/app/ondeviceintelligence/InferenceInfo.java b/core/java/android/app/ondeviceintelligence/InferenceInfo.java
new file mode 100644
index 0000000..5557a81
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/InferenceInfo.java
@@ -0,0 +1,211 @@
+/*
+ * 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.ondeviceintelligence;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents the information related to an inference event to track the resource usage
+ * as a function of inference time.
+ *
+ * @hide
+ */
+public class InferenceInfo implements Parcelable {
+
+    /**
+     * Uid for the caller app.
+     */
+    private final int uid;
+
+    /**
+     * Inference start time (milliseconds from the epoch time).
+     */
+    private final long startTimeMs;
+
+    /**
+     * Inference end time (milliseconds from the epoch time).
+     */
+    private final long endTimeMs;
+
+    /**
+     * Suspended time in milliseconds.
+     */
+    private final long suspendedTimeMs;
+
+    /**
+     * Constructs an InferenceInfo object with the specified parameters.
+     *
+     * @param uid             Uid for the caller app.
+     * @param startTimeMs     Inference start time (milliseconds from the epoch time).
+     * @param endTimeMs       Inference end time (milliseconds from the epoch time).
+     * @param suspendedTimeMs Suspended time in milliseconds.
+     */
+    public InferenceInfo(int uid, long startTimeMs, long endTimeMs,
+            long suspendedTimeMs) {
+        this.uid = uid;
+        this.startTimeMs = startTimeMs;
+        this.endTimeMs = endTimeMs;
+        this.suspendedTimeMs = suspendedTimeMs;
+    }
+
+    /**
+     * Constructs an InferenceInfo object from a Parcel.
+     *
+     * @param in The Parcel to read the object's data from.
+     */
+    protected InferenceInfo(Parcel in) {
+        uid = in.readInt();
+        startTimeMs = in.readLong();
+        endTimeMs = in.readLong();
+        suspendedTimeMs = in.readLong();
+    }
+
+
+    /**
+     * Writes the object's data to the provided Parcel.
+     *
+     * @param dest The Parcel to write the object's data to.
+     * @param flags Additional flags about how the object should be written.
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(uid);
+        dest.writeLong(startTimeMs);
+        dest.writeLong(endTimeMs);
+        dest.writeLong(suspendedTimeMs);
+    }
+
+    /**
+     * Returns the UID for the caller app.
+     *
+     * @return the UID for the caller app.
+     */
+    public int getUid() {
+        return uid;
+    }
+
+    /**
+     * Returns the inference start time in milliseconds from the epoch time.
+     *
+     * @return the inference start time in milliseconds from the epoch time.
+     */
+    public long getStartTimeMs() {
+        return startTimeMs;
+    }
+
+    /**
+     * Returns the inference end time in milliseconds from the epoch time.
+     *
+     * @return the inference end time in milliseconds from the epoch time.
+     */
+    public long getEndTimeMs() {
+        return endTimeMs;
+    }
+
+    /**
+     * Returns the suspended time in milliseconds.
+     *
+     * @return the suspended time in milliseconds.
+     */
+    public long getSuspendedTimeMs() {
+        return suspendedTimeMs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+
+    public static final @android.annotation.NonNull Parcelable.Creator<InferenceInfo> CREATOR
+            = new Parcelable.Creator<InferenceInfo>() {
+        @Override
+        public InferenceInfo[] newArray(int size) {
+            return new InferenceInfo[size];
+        }
+
+        @Override
+        public InferenceInfo createFromParcel(@android.annotation.NonNull Parcel in) {
+            return new InferenceInfo(in);
+        }
+    };
+
+    /**
+     * Builder class for creating instances of {@link InferenceInfo}.
+     */
+    public static class Builder {
+        private int uid;
+        private long startTimeMs;
+        private long endTimeMs;
+        private long suspendedTimeMs;
+
+        /**
+         * Sets the UID for the caller app.
+         *
+         * @param uid the UID for the caller app.
+         * @return the Builder instance.
+         */
+        public Builder setUid(int uid) {
+            this.uid = uid;
+            return this;
+        }
+
+        /**
+         * Sets the inference start time in milliseconds from the epoch time.
+         *
+         * @param startTimeMs the inference start time in milliseconds from the epoch time.
+         * @return the Builder instance.
+         */
+        public Builder setStartTimeMs(long startTimeMs) {
+            this.startTimeMs = startTimeMs;
+            return this;
+        }
+
+        /**
+         * Sets the inference end time in milliseconds from the epoch time.
+         *
+         * @param endTimeMs the inference end time in milliseconds from the epoch time.
+         * @return the Builder instance.
+         */
+        public Builder setEndTimeMs(long endTimeMs) {
+            this.endTimeMs = endTimeMs;
+            return this;
+        }
+
+        /**
+         * Sets the suspended time in milliseconds.
+         *
+         * @param suspendedTimeMs the suspended time in milliseconds.
+         * @return the Builder instance.
+         */
+        public Builder setSuspendedTimeMs(long suspendedTimeMs) {
+            this.suspendedTimeMs = suspendedTimeMs;
+            return this;
+        }
+
+        /**
+         * Builds and returns an instance of {@link InferenceInfo}.
+         *
+         * @return an instance of {@link InferenceInfo}.
+         */
+        public InferenceInfo build() {
+            return new InferenceInfo(uid, startTimeMs, endTimeMs,
+                    suspendedTimeMs);
+        }
+    }
+}
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
index a37f51b..937a9cd 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
+++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -496,6 +496,24 @@
         }
     }
 
+    /**
+     * This is primarily intended to be used to attribute/blame on-device intelligence power usage,
+     * via the configured remote implementation, to its actual caller.
+     *
+     * @param startTimeEpochMillis epoch millis used to filter the InferenceInfo events.
+     * @return InferenceInfo events since the passed in startTimeEpochMillis.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+        try {
+            return mService.getLatestInferenceInfo(startTimeEpochMillis);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
 
     /** Request inference with provided Bundle and Params. */
     public static final int REQUEST_TYPE_INFERENCE = 0;
diff --git a/core/java/android/app/pinner/OWNERS b/core/java/android/app/pinner/OWNERS
index 3e3fa66..fe5da9f 100644
--- a/core/java/android/app/pinner/OWNERS
+++ b/core/java/android/app/pinner/OWNERS
@@ -5,6 +5,5 @@
 philipcuadra@google.com
 shombert@google.com
 timmurray@google.com
-wessam@google.com
 jdduke@google.com
-shayba@google.com
\ No newline at end of file
+shayba@google.com
diff --git a/core/java/android/app/wearable/WearableSensingManager.java b/core/java/android/app/wearable/WearableSensingManager.java
index b2b14ce..d28969d 100644
--- a/core/java/android/app/wearable/WearableSensingManager.java
+++ b/core/java/android/app/wearable/WearableSensingManager.java
@@ -449,7 +449,7 @@
      * Consumer<android.service.voice.HotwordAudioStream>}, the system will check whether the {@link
      * android.service.voice.VoiceInteractionService} at that time is {@code
      * targetVisComponentName}. If not, the system will call {@link
-     * WearableSensingService#onActiveHotwordAudioStopRequested()} and will not forward the audio
+     * WearableSensingService#onStopHotwordAudioStream()} and will not forward the audio
      * data to the current {@link android.service.voice.HotwordDetectionService} nor {@link
      * android.service.voice.VoiceInteractionService}. The system will not send a status code to
      * {@code statusConsumer} regarding the {@code targetVisComponentName} check. The caller is
@@ -464,9 +464,9 @@
      * continue to use the previous consumers after receiving a new one.
      *
      * <p>If the {@code statusConsumer} returns {@link STATUS_SUCCESS}, the caller should call
-     * {@link #stopListeningForHotword(Executor, Consumer)} when it wants the wearable to stop
+     * {@link #stopHotwordRecognition(Executor, Consumer)} when it wants the wearable to stop
      * listening for hotword. If the {@code statusConsumer} returns any other status code, a failure
-     * has occurred and calling {@link #stopListeningForHotword(Executor, Consumer)} is not
+     * has occurred and calling {@link #stopHotwordRecognition(Executor, Consumer)} is not
      * required. The system will not retry listening automatically. The caller should call this
      * method again if they want to retry.
      *
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 8fe5ae0..b4ad1c8 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -1149,6 +1149,32 @@
         }
     }
 
+    /**
+     * Remove bonding between this device and an associated companion device.
+     *
+     * <p>This is an asynchronous call, it will return immediately. Register for {@link
+     * BluetoothDevice#ACTION_BOND_STATE_CHANGED} intents to be notified when the bond removal
+     * process completes, and its result.
+     *
+     * @param associationId an already-associated companion device to remove bond from
+     * @return false on immediate error, true if bond removal process will begin
+     */
+    @FlaggedApi(Flags.FLAG_UNPAIR_ASSOCIATED_DEVICE)
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+    public boolean removeBond(int associationId) {
+        if (mService == null) {
+            Log.w(TAG, "CompanionDeviceManager service is not available.");
+            return false;
+        }
+
+        try {
+            return mService.removeBond(associationId, mContext.getOpPackageName(),
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     // TODO(b/315163162) Add @Deprecated keyword after 24Q2 cut.
     /**
      * Register to receive callbacks whenever the associated device comes in and out of range.
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 1b00f90e..de3ddec 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -141,4 +141,7 @@
     byte[] getBackupPayload(int userId);
 
     void applyRestoredPayload(in byte[] payload, int userId);
+
+    @EnforcePermission("BLUETOOTH_CONNECT")
+    boolean removeBond(int associationId, in String packageName, int userId);
 }
diff --git a/core/java/android/companion/ObservingDevicePresenceRequest.java b/core/java/android/companion/ObservingDevicePresenceRequest.java
index 11ea735..3150b87 100644
--- a/core/java/android/companion/ObservingDevicePresenceRequest.java
+++ b/core/java/android/companion/ObservingDevicePresenceRequest.java
@@ -180,6 +180,9 @@
          * <p>Calling apps must use either this API or {@link #setAssociationId(int)},
          * but not both.</p>
          *
+         * <p>Calling app must hold the
+         * {@link AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION} profile.</p>
+         *
          * @param uuid The ParcelUuid for observing device presence.
          */
         @NonNull
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index 36d0e08..fd4ba83 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -46,4 +46,11 @@
     namespace: "companion"
     description: "Enable ongoing perm sync"
     bug: "338469649"
+}
+
+flag {
+    name: "unpair_associated_device"
+    namespace: "companion"
+    description: "Unpair with an associated bluetooth device"
+    bug: "322237619"
 }
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 30a1135..24f18cc 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -281,5 +281,5 @@
      * Returns the id of the virtual camera with given config.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    int getVirtualCameraId(in VirtualCameraConfig camera);
+    String getVirtualCameraId(in VirtualCameraConfig camera);
 }
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 00d5343..60448ba 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -378,8 +378,8 @@
     VirtualCamera createVirtualCamera(@NonNull VirtualCameraConfig config) {
         try {
             mVirtualDevice.registerVirtualCamera(config);
-            return new VirtualCamera(mVirtualDevice,
-                            Integer.toString(mVirtualDevice.getVirtualCameraId(config)), config);
+            return new VirtualCamera(mVirtualDevice, mVirtualDevice.getVirtualCameraId(config),
+                    config);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bad73fc..4fcf6b6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7188,9 +7188,10 @@
      * as the package remains in the foreground, or has any active manifest components (e.g. when
      * another app is accessing a content provider in the package).
      * <p>
-     * If you want to revoke the permissions right away, you could call {@code System.exit()}, but
-     * this could affect other apps that are accessing your app at the moment. For example, apps
-     * accessing a content provider in your app will all crash.
+     * If you want to revoke the permissions right away, you could call {@code System.exit()} in
+     * {@code Handler.postDelayed} with a delay to allow completion of async IPC, But
+     * {@code System.exit()} could affect other apps that are accessing your app at the moment.
+     * For example, apps accessing a content provider in your app will all crash.
      * <p>
      * Note that the settings UI shows a permission group as granted as long as at least one
      * permission in the group is granted. If you want the user to observe the revocation in the
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 02d62a2..dfa29738 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -106,7 +106,6 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.TimeZone;
-import java.util.function.Consumer;
 
 /**
  * An intent is an abstract description of an operation to be performed.  It
@@ -6110,11 +6109,17 @@
      * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)}
      * method will contains the original intent Chooser has been launched with under the
      * {@link #EXTRA_INTENT} key as a context for the current sharing session. The returned
-     * {@link android.database.Cursor} should contain
-     * {@link android.service.chooser.AdditionalContentContract.Columns#URI} column for the item URI
-     * and, optionally, {@link AdditionalContentContract.CursorExtraKeys#POSITION} extra that
+     * {@link android.database.Cursor} should contain:
+     * <ul>
+     * <li>{@link android.service.chooser.AdditionalContentContract.Columns#URI} column for the item
+     * URI.</li>
+     * <li>Optional columns {@link MediaStore.MediaColumns#WIDTH} and
+     * {@link MediaStore.MediaColumns#HEIGHT} for the dimensions of the preview image.
+     * These columns can also be returned for each {@link #EXTRA_STREAM} item metadata
+     * {@link ContentProvider#query(Uri, String[], Bundle, CancellationSignal)} call.</li>
+     * <li>Optional {@link AdditionalContentContract.CursorExtraKeys#POSITION} extra that
      * specifies the cursor starting position; the item at this position is expected to match the
-     * item specified by {@link #EXTRA_CHOOSER_FOCUSED_ITEM_POSITION}.</p>
+     * item specified by {@link #EXTRA_CHOOSER_FOCUSED_ITEM_POSITION}.</li></ul></p>
      *
      * <p>When the user makes a selection change,
      * {@link ContentProvider#call(String, String, Bundle)} method will be invoked with the "method"
@@ -8330,27 +8335,6 @@
         }
     }
 
-    /**
-     * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission
-     * grants will need to be issued to ensure the recipient of this object is able to render its
-     * contents.
-     * See b/281044385 for more context and examples about what happens when this isn't done
-     * correctly.
-     *
-     * @hide
-     */
-    public void visitUris(@NonNull Consumer<Uri> visitor) {
-        if (android.app.Flags.visitRiskyUris()) {
-            visitor.accept(mData);
-            if (mSelector != null) {
-                mSelector.visitUris(visitor);
-            }
-            if (mOriginalIntent != null) {
-                mOriginalIntent.visitUris(visitor);
-            }
-        }
-    }
-
     public static Intent getIntentOld(String uri) throws URISyntaxException {
         Intent intent = getIntentOld(uri, 0);
         intent.mLocalFlags |= LOCAL_FLAG_FROM_URI;
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 86d061c..e895d7b 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -823,6 +823,16 @@
     }
 
     /**
+     * Returns the number of actions in the filter, or {@code 0} if there are no actions.
+     * <p> This method provides a safe alternative to {@link #countActions()}, which
+     * may throw an exception if there are no actions.
+     * @hide
+     */
+    public final int safeCountActions() {
+        return mActions == null ? 0 : mActions.size();
+    }
+
+    /**
      * Return an action in the filter.
      */
     public final String getAction(int index) {
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 57ee622..bf51f00 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -304,6 +304,7 @@
      * <li>It is not equal to the calling user</li>
      * <li>It is in the same profile group of calling user profile</li>
      * <li>It is enabled</li>
+     * <li>It is not hidden (ex. profile type {@link UserManager#USER_TYPE_PROFILE_PRIVATE})</li>
      * </ul>
      *
      * @see UserManager#getUserProfiles()
@@ -460,8 +461,8 @@
      *
      * <p>Specifically, returns whether the following are all true:
      * <ul>
-     * <li>{@code UserManager#getEnabledProfileIds(int)} returns at least one other profile for the
-     * calling user.</li>
+     * <li>{@code UserManager#getProfileIdsExcludingHidden(int)} returns at least one other
+     * profile for the calling user.</li>
      * <li>The calling app has requested
      * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.</li>
      * <li>The calling app is not a profile owner within the profile group of the calling user.</li>
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 6168b68..df27f9d 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -695,15 +695,12 @@
      * <p>If the caller is running on a managed profile, it'll return only the current profile.
      * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
      *
-     * <p>To get hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>To get hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -764,20 +761,17 @@
      * list.</li>
      * </ul>
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param packageName The specific package to query. If null, it checks all installed packages
      *            in the profile.
      * @param user The UserHandle of the profile.
      * @return List of launchable activities. Can be an empty list but will not be null.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -820,19 +814,16 @@
      * Returns information related to a user which is useful for displaying UI elements
      * to distinguish it from other users (eg, badges).
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param userHandle user handle of the user for which LauncherUserInfo is requested.
      * @return the {@link LauncherUserInfo} object related to the user specified, null in case
      * the user is inaccessible.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @Nullable
     @SuppressLint("RequiresPermission")
     @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
@@ -873,14 +864,9 @@
      * </ul>
      * </p>
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param packageName the package for which intent sender to launch App Market Activity is
      *                    required.
@@ -888,6 +874,8 @@
      * @return {@link IntentSender} object which launches the App Market Activity, null in case
      *         there is no such activity.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @Nullable
     @SuppressLint("RequiresPermission")
     @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
@@ -913,19 +901,16 @@
      * <p>An empty list denotes that all system packages should be treated as pre-installed for that
      * user at creation.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param userHandle the user for which installed system packages are required.
      * @return {@link List} of {@link String}, representing the package name of the installed
      *        package. Can be empty but not null.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
     @NonNull
     @SuppressLint("RequiresPermission")
@@ -945,13 +930,15 @@
     /**
      * Returns {@link IntentSender} which can be used to start the Private Space Settings Activity.
      *
-     * <p> Caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the
+     * <p> Caller should have {@link android.app.role.RoleManager#ROLE_HOME} and either of the
      * permissions required.</p>
      *
      * @return {@link IntentSender} object which launches the Private Space Settings Activity, if
      * successful, null otherwise.
      * @hide
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @Nullable
     @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE)
     @RequiresPermission(conditional = true,
@@ -968,19 +955,16 @@
      * Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
      * returns null.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param intent The intent to find a match for.
      * @param user The profile to look in for a match.
      * @return An activity info object if there is a match.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1033,20 +1017,17 @@
     /**
      * Starts a Main activity in the specified profile.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param component The ComponentName of the activity to launch
      * @param user The UserHandle of the profile
      * @param sourceBounds The Rect containing the source bounds of the clicked icon
      * @param opts Options to pass to startActivity
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1087,20 +1068,17 @@
      * Starts the settings activity to show the application details for a
      * package in the specified profile.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param component The ComponentName of the package to launch settings for.
      * @param user The UserHandle of the profile
      * @param sourceBounds The Rect containing the source bounds of the clicked icon
      * @param opts Options to pass to startActivity
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1215,20 +1193,17 @@
     /**
      * Checks if the package is installed and enabled for a profile.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param packageName The package to check.
      * @param user The UserHandle of the profile.
      *
      * @return true if the package exists and is enabled.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1249,14 +1224,9 @@
      * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
      * app and the launcher.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * <p>Note: This just returns whatever extras were provided to the system, <em>which might
      * even be {@code null}.</em>
@@ -1269,6 +1239,8 @@
      * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
      * @see PackageManager#isPackageSuspended()
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1286,19 +1258,16 @@
      * could be done because the package was marked as distracting to the user via
      * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param packageName The package for which to check.
      * @param user the {@link UserHandle} of the profile.
      * @return
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1316,14 +1285,9 @@
     /**
      * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param packageName The package name of the application
      * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
@@ -1333,6 +1297,8 @@
      *         {@code null} if the package isn't installed for the given profile, or the profile
      *         isn't enabled.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1385,20 +1351,17 @@
      * <p>The activity may still not be exported, in which case {@link #startMainActivity} will
      * throw a {@link SecurityException} unless the caller has the same UID as the target app's.
      *
-     * <p>If the user in question is a hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param component The activity to check.
      * @param user The UserHandle of the profile.
      *
      * @return true if the activity exists and is enabled.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1960,17 +1923,14 @@
     /**
      * Registers a callback for changes to packages in this user and managed profiles.
      *
-     * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param callback The callback to register.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -1981,18 +1941,15 @@
     /**
      * Registers a callback for changes to packages in this user and managed profiles.
      *
-     * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @param callback The callback to register.
      * @param handler that should be used to post callbacks on, may be null.
      */
+    // Alternatively, a system app can access this api for private profile if they've been granted
+    // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission.
     @SuppressLint("RequiresPermission")
     @RequiresPermission(conditional = true,
             anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
@@ -2446,14 +2403,9 @@
      * package name in the app's manifest, have the android.permission.QUERY_ALL_PACKAGES, or be
      * the session owner to retrieve these details.
      *
-     * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
-     * caller should have either:</p>
-     * <ul>
-     * <li>the privileged {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES}
-     * permission</li>
-     * <li>the normal {@link android.Manifest.permission.ACCESS_HIDDEN_PROFILES} permission and the
-     * {@link android.app.role.RoleManager.ROLE_HOME} role. </li>
-     * </ul>
+     * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE},
+     * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES}
+     * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role.
      *
      * @see PackageInstaller#getAllSessions()
      */
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 821034a..c673d58 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2797,6 +2797,8 @@
         public int developmentInstallFlags = 0;
         /** {@hide} */
         public int unarchiveId = -1;
+        /** {@hide} */
+        public @Nullable String dexoptCompilerFilter = null;
 
         private final ArrayMap<String, Integer> mPermissionStates;
 
@@ -2850,6 +2852,7 @@
             applicationEnabledSettingPersistent = source.readBoolean();
             developmentInstallFlags = source.readInt();
             unarchiveId = source.readInt();
+            dexoptCompilerFilter = source.readString();
         }
 
         /** {@hide} */
@@ -2885,6 +2888,7 @@
             ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
             ret.developmentInstallFlags = developmentInstallFlags;
             ret.unarchiveId = unarchiveId;
+            ret.dexoptCompilerFilter = dexoptCompilerFilter;
             return ret;
         }
 
@@ -3564,6 +3568,11 @@
         }
 
         /** @hide */
+        public void setDexoptCompilerFilter(@Nullable String dexoptCompilerFilter) {
+            this.dexoptCompilerFilter = dexoptCompilerFilter;
+        }
+
+        /** @hide */
         @NonNull
         public ArrayMap<String, Integer> getPermissionStates() {
             return mPermissionStates;
@@ -3622,6 +3631,7 @@
                     applicationEnabledSettingPersistent);
             pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
             pw.printPair("unarchiveId", unarchiveId);
+            pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter);
             pw.println();
         }
 
@@ -3667,6 +3677,7 @@
             dest.writeBoolean(applicationEnabledSettingPersistent);
             dest.writeInt(developmentInstallFlags);
             dest.writeInt(unarchiveId);
+            dest.writeString(dexoptCompilerFilter);
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1f6730b..4b579e7 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,15 +2628,6 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
-        // STOPSHIP: hack for the pre-release SDK
-        if (platformSdkCodenames.length == 0
-                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
-                targetCode)) {
-            Slog.w(TAG, "Package requires development platform " + targetCode
-                    + ", returning current version " + Build.VERSION.SDK_INT);
-            return Build.VERSION.SDK_INT;
-        }
-
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + targetCode
@@ -2708,15 +2699,6 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
-        // STOPSHIP: hack for the pre-release SDK
-        if (platformSdkCodenames.length == 0
-                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
-                minCode)) {
-            Slog.w(TAG, "Package requires min development platform " + minCode
-                    + ", returning current version " + Build.VERSION.SDK_INT);
-            return Build.VERSION.SDK_INT;
-        }
-
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 5668c54..a9c07d1 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -267,3 +267,13 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+  name: "use_private_space_icon_in_biometric_prompt"
+  namespace: "profile_experiences"
+  description: "Update the biometric prompt from generic Settings icon to private space icon"
+  bug: "333528540"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index c7403c0..153dd9a 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,15 +316,6 @@
             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
-        // STOPSHIP: hack for the pre-release SDK
-        if (platformSdkCodenames.length == 0
-                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
-                        minCode)) {
-            Slog.w(TAG, "Parsed package requires min development platform " + minCode
-                    + ", returning current version " + Build.VERSION.SDK_INT);
-            return input.success(Build.VERSION.SDK_INT);
-        }
-
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -377,27 +368,19 @@
             return input.success(targetVers);
         }
 
-        // If it's a pre-release SDK and the codename matches this platform, it
-        // definitely targets this SDK.
-        if (matchTargetCode(platformSdkCodenames, targetCode)) {
-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
-        }
-
-        // STOPSHIP: hack for the pre-release SDK
-        if (platformSdkCodenames.length == 0
-                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
-                        targetCode)) {
-            Slog.w(TAG, "Parsed package requires development platform " + targetCode
-                    + ", returning current version " + Build.VERSION.SDK_INT);
-            return input.success(Build.VERSION.SDK_INT);
-        }
-
         try {
             if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
                 return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
             }
         } catch (IllegalArgumentException e) {
-            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
+            // isAtMost() throws it when encountering an older SDK codename
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
+        }
+
+        // If it's a pre-release SDK and the codename matches this platform, it
+        // definitely targets this SDK.
+        if (matchTargetCode(platformSdkCodenames, targetCode)) {
+            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
         // Otherwise, we're looking at an incompatible pre-release SDK.
diff --git a/core/java/android/credentials/CredentialManager.java b/core/java/android/credentials/CredentialManager.java
index 481ff2e..f0f691d 100644
--- a/core/java/android/credentials/CredentialManager.java
+++ b/core/java/android/credentials/CredentialManager.java
@@ -470,8 +470,8 @@
      * Returns {@code true} if the calling application provides a CredentialProviderService that is
      * enabled for the current user, or {@code false} otherwise. CredentialProviderServices are
      * enabled on a per-service basis so the individual component name of the service should be
-     * passed in here. <strong>Usage of this API is discouraged as it is not fully functional, and
-     * may throw a NullPointerException on certain devices and/or API versions.</strong>
+     * passed in here. <strong>Usage of this API is encouraged in API level 35 and above. It
+     * may throw a NullPointerException on certain devices running other API versions.</strong>
      *
      * @throws IllegalArgumentException if the componentName package does not match the calling
      * package name this call will throw an exception
diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
old mode 100755
new mode 100644
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 61f1ee1..37a2df8 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,6 +38,9 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Binder;
@@ -193,7 +196,14 @@
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) {
-            mPromptInfo.setLogoRes(logoRes);
+            if (mPromptInfo.getLogoBitmap() != null) {
+                throw new IllegalStateException(
+                        "Exclusively one of logo resource or logo bitmap can be set");
+            }
+            if (logoRes != 0) {
+                mPromptInfo.setLogo(logoRes,
+                        convertDrawableToBitmap(mContext.getDrawable(logoRes)));
+            }
             return this;
         }
 
@@ -212,7 +222,11 @@
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         @NonNull
         public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
-            mPromptInfo.setLogoBitmap(logoBitmap);
+            if (mPromptInfo.getLogoRes() != 0) {
+                throw new IllegalStateException(
+                        "Exclusively one of logo resource or logo bitmap can be set");
+            }
+            mPromptInfo.setLogo(0, logoBitmap);
             return this;
         }
 
@@ -821,7 +835,7 @@
      * Gets the drawable resource of the logo for the prompt, as set by
      * {@link Builder#setLogoRes(int)}. Currently for system applications use only.
      *
-     * @return The drawable resource of the logo, or -1 if the prompt has no logo resource set.
+     * @return The drawable resource of the logo, or 0 if the prompt has no logo resource set.
      */
     @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@@ -1516,4 +1530,29 @@
     private static boolean isCredentialAllowed(@Authenticators.Types int allowedAuthenticators) {
         return (allowedAuthenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
     }
+
+    /** Converts {@code drawable} to a {@link Bitmap}. */
+    private static Bitmap convertDrawableToBitmap(Drawable drawable) {
+        if (drawable == null) {
+            return null;
+        }
+
+        if (drawable instanceof BitmapDrawable) {
+            return ((BitmapDrawable) drawable).getBitmap();
+        }
+
+        Bitmap bitmap;
+        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
+            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+            // Single color bitmap will be created of 1x1 pixel
+        } else {
+            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                    drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        }
+
+        final Canvas canvas = new Canvas(bitmap);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+        return bitmap;
+    }
 }
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index bb07b9b..ba9f30d 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -33,7 +33,7 @@
  */
 public class PromptInfo implements Parcelable {
 
-    @DrawableRes private int mLogoRes = -1;
+    @DrawableRes private int mLogoRes;
     @Nullable private Bitmap mLogoBitmap;
     @Nullable private String mLogoDescription;
     @NonNull private CharSequence mTitle;
@@ -187,7 +187,7 @@
      * this permission.
      */
     public boolean requiresAdvancedPermission() {
-        if (mLogoRes != -1) {
+        if (mLogoRes != 0) {
             return true;
         } else if (mLogoBitmap != null) {
             return true;
@@ -217,14 +217,17 @@
     // LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)
 
     // Setters
-    public void setLogoRes(@DrawableRes int logoRes) {
-        mLogoRes = logoRes;
-        checkOnlyOneLogoSet();
-    }
 
-    public void setLogoBitmap(@NonNull Bitmap logoBitmap) {
+    /**
+     * Sets logo res and bitmap
+     *
+     * @param logoRes    The logo res set by the app; Or 0 if the app sets bitmap directly.
+     * @param logoBitmap The bitmap from logoRes if the app sets logoRes; Or the bitmap set by the
+     *                   app directly.
+     */
+    public void setLogo(@DrawableRes int logoRes, @NonNull Bitmap logoBitmap) {
+        mLogoRes = logoRes;
         mLogoBitmap = logoBitmap;
-        checkOnlyOneLogoSet();
     }
 
     public void setLogoDescription(@NonNull String logoDescription) {
@@ -326,13 +329,29 @@
     }
 
     // Getters
+
+    /**
+     * Returns the logo bitmap either from logo resource or bitmap passed in from the app.
+     */
+    public Bitmap getLogo() {
+        return mLogoBitmap;
+    }
+
+    /**
+     * Returns the logo res set by the app.
+     */
     @DrawableRes
     public int getLogoRes() {
         return mLogoRes;
     }
 
+    /**
+     * Returns the logo bitmap set by the app.
+     */
     public Bitmap getLogoBitmap() {
-        return mLogoBitmap;
+        // If mLogoRes has been set, return null since mLogoBitmap is from the res, but not from
+        // the app directly.
+        return mLogoRes == 0 ? mLogoBitmap : null;
     }
 
     public String getLogoDescription() {
@@ -436,10 +455,4 @@
         return mComponentNameForConfirmDeviceCredentialActivity;
     }
 
-    private void checkOnlyOneLogoSet() {
-        if (mLogoRes != -1 && mLogoBitmap != null) {
-            throw new IllegalStateException(
-                    "Exclusively one of logo resource or logo bitmap can be set");
-        }
-    }
 }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 3cc87ea..6fffb82 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1472,9 +1472,9 @@
             new Key<Integer>("android.flash.info.strengthDefaultLevel", int.class);
 
     /**
-     * <p>Maximum flash brightness level for manual flash control in SINGLE mode.</p>
+     * <p>Maximum flash brightness level for manual flash control in <code>SINGLE</code> mode.</p>
      * <p>Maximum flash brightness level in camera capture mode and
-     * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to SINGLE.
+     * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to <code>SINGLE</code>.
      * Value will be &gt; 1 if the manual flash strength control feature is supported,
      * otherwise the value will be equal to 1.
      * Note that this level is just a number of supported levels (the granularity of control).
@@ -1490,7 +1490,7 @@
             new Key<Integer>("android.flash.singleStrengthMaxLevel", int.class);
 
     /**
-     * <p>Default flash brightness level for manual flash control in SINGLE mode.</p>
+     * <p>Default flash brightness level for manual flash control in <code>SINGLE</code> mode.</p>
      * <p>If flash unit is available this will be greater than or equal to 1 and less
      * or equal to {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
      * Note for devices that do not support the manual flash strength control
@@ -1506,9 +1506,9 @@
             new Key<Integer>("android.flash.singleStrengthDefaultLevel", int.class);
 
     /**
-     * <p>Maximum flash brightness level for manual flash control in TORCH mode</p>
+     * <p>Maximum flash brightness level for manual flash control in <code>TORCH</code> mode</p>
      * <p>Maximum flash brightness level in camera capture mode and
-     * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to TORCH.
+     * {@link CaptureRequest#FLASH_MODE android.flash.mode} set to <code>TORCH</code>.
      * Value will be &gt; 1 if the manual flash strength control feature is supported,
      * otherwise the value will be equal to 1.</p>
      * <p>Note that this level is just a number of supported levels(the granularity of control).
@@ -1530,7 +1530,7 @@
             new Key<Integer>("android.flash.torchStrengthMaxLevel", int.class);
 
     /**
-     * <p>Default flash brightness level for manual flash control in TORCH mode</p>
+     * <p>Default flash brightness level for manual flash control in <code>TORCH</code> mode</p>
      * <p>If flash unit is available this will be greater than or equal to 1 and less
      * or equal to {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
      * Note for the devices that do not support the manual flash strength control feature,
@@ -4152,10 +4152,16 @@
     /**
      * <p>Whether the RAW images output from this camera device are subject to
      * lens shading correction.</p>
-     * <p>If TRUE, all images produced by the camera device in the RAW image formats will
-     * have lens shading correction already applied to it. If FALSE, the images will
-     * not be adjusted for lens shading correction.
-     * See {@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW android.request.maxNumOutputRaw} for a list of RAW image formats.</p>
+     * <p>If <code>true</code>, all images produced by the camera device in the <code>RAW</code> image formats will have
+     * at least some lens shading correction already applied to it. If <code>false</code>, the images will
+     * not be adjusted for lens shading correction.  See {@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW android.request.maxNumOutputRaw} for a
+     * list of RAW image formats.</p>
+     * <p>When <code>true</code>, the <code>lensShadingCorrectionMap</code> key may still have values greater than 1.0,
+     * and those will need to be applied to any captured RAW frames for them to match the shading
+     * correction of processed buffers such as <code>YUV</code> or <code>JPEG</code> images. This may occur, for
+     * example, when some basic fixed lens shading correction is applied by hardware to RAW data,
+     * and additional correction is done dynamically in the camera processing pipeline after
+     * demosaicing.</p>
      * <p>This key will be <code>null</code> for all devices do not report this information.
      * Devices with RAW capability will always report this information in this key.</p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index dfbf06b..7665fe8 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -24,6 +24,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.ServiceConnection;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics.Key;
@@ -278,6 +279,8 @@
         private final Object mLock = new Object();
         private final int PROXY_SERVICE_DELAY_MS = 2000;
         private ExtensionConnectionManager mConnectionManager = new ExtensionConnectionManager();
+        private boolean mPermissionForFallbackEnabled = false;
+        private boolean mIsFallbackEnabled = false;
 
         // Singleton, don't allow construction
         private CameraExtensionManagerGlobal() {}
@@ -325,6 +328,7 @@
                                 "Choosing the fallback software implementation service: "
                                 + serviceName);
                         intent.setClassName(packageName, serviceName);
+                        mIsFallbackEnabled = true;
                     }
                 }
 
@@ -436,6 +440,20 @@
                     releaseProxyConnectionLocked(ctx, extension);
                 }
 
+                if (Flags.concertMode() && ret && useFallback && mIsFallbackEnabled) {
+                    try {
+                        InitializeSessionHandler cb = new InitializeSessionHandler(ctx);
+                        initializeSession(cb, extension);
+                        ret = mPermissionForFallbackEnabled;
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Failed to initialize extension. Extension service does not"
+                                + " respond!");
+                        ret = false;
+                    } finally {
+                        releaseSession(extension);
+                    }
+                }
+
                 return ret;
             }
         }
@@ -464,7 +482,6 @@
                 if (!vendorImpl) {
                     unregisterClient(ctx, token, extension);
                     ret = registerClientHelper(ctx, token, extension, true /*useFallback*/);
-
                 }
             }
 
@@ -508,6 +525,7 @@
                     try {
                         mConnectionManager.getProxy(extension).releaseSession();
                         mConnectionManager.setSessionInitialized(false);
+                        mPermissionForFallbackEnabled = false; // Reset permission status
                     } catch (RemoteException e) {
                         Log.e(TAG, "Failed to release session! Extension service does"
                                 + " not respond!");
@@ -556,6 +574,52 @@
             }
         }
 
+        private class InitializeSessionHandler extends IInitializeSessionCallback.Stub {
+            private Context mContext;
+
+            public InitializeSessionHandler(Context context) {
+                mContext = context;
+            }
+
+            @Override
+            public void onSuccess() {
+                // Verify that the camera permission is granted if using
+                // the fallback implementation for an extension
+                String[] callingUidPackages = mContext.getPackageManager()
+                        .getPackagesForUid(Binder.getCallingUid());
+                String fallbackPackageName = mContext.getResources()
+                        .getString(FALLBACK_PACKAGE_NAME);
+
+                if (!fallbackPackageName.isEmpty()
+                        && Arrays.stream(callingUidPackages)
+                        .anyMatch(fallbackPackageName::equals)) {
+                    String[] cameraPermissions = {
+                        android.Manifest.permission.SYSTEM_CAMERA,
+                        android.Manifest.permission.CAMERA
+                    };
+
+                    boolean allPermissionsGranted = true;
+                    for (String permission : cameraPermissions) {
+                        int permissionResult = mContext.checkPermission(permission,
+                                Binder.getCallingPid(), Binder.getCallingUid());
+                        if (permissionResult != PackageManager.PERMISSION_GRANTED) {
+                            Log.w(TAG, permission + " permission not granted for "
+                                    + fallbackPackageName + ", permission check result: "
+                                    + permissionResult);
+                            allPermissionsGranted = false;
+                        }
+                    }
+
+                    mPermissionForFallbackEnabled = allPermissionsGranted;
+                }
+            }
+
+            @Override
+            public void onFailure() {
+                Log.e(TAG, "Failed to initialize proxy service session!");
+            }
+        }
+
         private class ExtensionConnectionManager {
             // Maps extension to ExtensionConnection
             private Map<Integer, ExtensionConnection> mConnections = new HashMap<>();
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index de26384..4819f67 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2360,11 +2360,8 @@
      * <p>If the session configuration is not supported, the AE mode reported in the
      * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
      * <p>When this AE mode is enabled, the CaptureResult field
-     * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the
-     * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p>
-     * <p>The application can observe the CaptureResult field
-     * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or
-     * 'INACTIVE'.</p>
+     * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will indicate when low light boost is 'ACTIVE'
+     * or 'INACTIVE'. By default {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be 'INACTIVE'.</p>
      * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
      * upper bound lux value defined by {@link CameraCharacteristics#CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE android.control.lowLightBoostInfoLuminanceRange}.
      * This mode will be 'INACTIVE' once the scene lighting condition is greater than the
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 938636f..6968f27 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2683,27 +2683,27 @@
     /**
      * <p>Flash strength level to be used when manual flash control is active.</p>
      * <p>Flash strength level to use in capture mode i.e. when the applications control
-     * flash with either SINGLE or TORCH mode.</p>
+     * flash with either <code>SINGLE</code> or <code>TORCH</code> mode.</p>
      * <p>Use {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
      * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} to check whether the device supports
      * flash strength control or not.
-     * If the values of android.flash.info.singleStrengthMaxLevel and
+     * If the values of {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
      * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1,
      * then the device supports manual flash strength control.</p>
-     * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be &gt;= 1
+     * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>TORCH</code> the value must be &gt;= 1
      * and &lt;= {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
      * If the application doesn't set the key and
      * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} &gt; 1,
      * then the flash will be fired at the default level set by HAL in
      * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL android.flash.torchStrengthDefaultLevel}.
-     * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be &gt;= 1
+     * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>SINGLE</code>, then the value must be &gt;= 1
      * and &lt;= {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
      * If the application does not set this key and
      * {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} &gt; 1,
      * then the flash will be fired at the default level set by HAL
      * in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
-     * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
-     * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p>
+     * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
+     * <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
      * <p><b>Range of valid values:</b><br>
      * <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to TORCH;
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 4406a41..d652b4c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2819,12 +2819,11 @@
      * <p>When low light boost is enabled by setting the AE mode to
      * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
      * boost when the light level threshold is exceeded.</p>
-     * <p>This field is present in the CaptureResult when the AE mode is set to
-     * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p>
      * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
      * indicate when it is not being applied by returning 'INACTIVE'.</p>
      * <p>This key will be absent from the CaptureResult if AE mode is not set to
      * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
+     * <p>The default value will always be 'INACTIVE'.</p>
      * <p><b>Possible values:</b></p>
      * <ul>
      *   <li>{@link #CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE INACTIVE}</li>
@@ -2976,27 +2975,27 @@
     /**
      * <p>Flash strength level to be used when manual flash control is active.</p>
      * <p>Flash strength level to use in capture mode i.e. when the applications control
-     * flash with either SINGLE or TORCH mode.</p>
+     * flash with either <code>SINGLE</code> or <code>TORCH</code> mode.</p>
      * <p>Use {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
      * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} to check whether the device supports
      * flash strength control or not.
-     * If the values of android.flash.info.singleStrengthMaxLevel and
+     * If the values of {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
      * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1,
      * then the device supports manual flash strength control.</p>
-     * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be &gt;= 1
+     * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>TORCH</code> the value must be &gt;= 1
      * and &lt;= {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
      * If the application doesn't set the key and
      * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} &gt; 1,
      * then the flash will be fired at the default level set by HAL in
      * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL android.flash.torchStrengthDefaultLevel}.
-     * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be &gt;= 1
+     * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> <code>SINGLE</code>, then the value must be &gt;= 1
      * and &lt;= {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
      * If the application does not set this key and
      * {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} &gt; 1,
      * then the flash will be fired at the default level set by HAL
      * in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
-     * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
-     * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p>
+     * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
+     * <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
      * <p><b>Range of valid values:</b><br>
      * <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to TORCH;
@@ -4846,6 +4845,9 @@
      * correction map that needs to be applied to get shading
      * corrected images that match the camera device's output for
      * non-RAW formats.</p>
+     * <p>Therefore, whatever the value of lensShadingApplied is, the lens
+     * shading map should always be applied to RAW images if the goal is to
+     * match the shading appearance of processed (non-RAW) images.</p>
      * <p>For a complete shading correction map, the least shaded
      * section of the image will have a gain factor of 1; all
      * other sections will have gains above 1.</p>
diff --git a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java
index c33956b..b681ce4 100644
--- a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java
+++ b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java
@@ -18,7 +18,6 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
-import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureRequest.Key;
 import android.hardware.camera2.impl.ExtensionKey;
@@ -43,6 +42,9 @@
 @FlaggedApi(Flags.FLAG_CONCERT_MODE_API)
 public final class ExtensionCaptureRequest {
 
+    /** To avoid exposing constructor */
+    private ExtensionCaptureRequest() {}
+
     /**
      * <p>Used to apply an additional digital zoom factor for the
      * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
diff --git a/core/java/android/hardware/camera2/ExtensionCaptureResult.java b/core/java/android/hardware/camera2/ExtensionCaptureResult.java
index 95feb2f..b7ba78c 100644
--- a/core/java/android/hardware/camera2/ExtensionCaptureResult.java
+++ b/core/java/android/hardware/camera2/ExtensionCaptureResult.java
@@ -18,7 +18,6 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
-import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraExtensionCharacteristics;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.CaptureResult.Key;
@@ -45,6 +44,9 @@
 @FlaggedApi(Flags.FLAG_CONCERT_MODE_API)
 public final class ExtensionCaptureResult {
 
+    /** To avoid exposing constructor */
+    private ExtensionCaptureResult() {}
+
    /**
      * <p>The padding region for the
      * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY }
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
index 8898a4c..df057a1 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
@@ -105,7 +105,8 @@
 
             try {
                 return cameraService.isSessionConfigurationWithParametersSupported(mCameraId,
-                        mTargetSdkVersion, config, mContext.getDeviceId(),
+                        mTargetSdkVersion, config,
+                        mContext.getDeviceId(),
                         mCameraManager.getDevicePolicyFromContext(mContext));
             } catch (ServiceSpecificException e) {
                 throw ExceptionUtils.throwAsPublicException(e);
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index dda52dd..ebcc371 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -638,13 +638,15 @@
 
     /**
      * Create a list of {@link OutputConfiguration} instances for a
-     * {@link android.hardware.camera2.params.MultiResolutionImageReader}.
+     * {@link MultiResolutionImageReader}.
      *
      * <p>This method can be used to create query OutputConfigurations for a
      * MultiResolutionImageReader that can be included in a SessionConfiguration passed into
-     * {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up
-     * a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput}
-     * can be used to link to the actual MultiResolutionImageReader.</p>
+     * {@link
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+     * before opening and setting up a camera device in full, at which point {@link
+     * #setSurfacesForMultiResolutionOutput} can be used to link to the actual
+     * MultiResolutionImageReader.</p>
      *
      * <p>This constructor takes same arguments used to create a {@link
      * MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo}
@@ -655,12 +657,12 @@
      * @param format The format of the MultiResolutionImageReader. This must be one of the {@link
      *               android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants
      *               supported by the camera device. Note that not all formats are supported, like
-     *               {@link ImageFormat.NV21}. The supported multi-resolution reader format can be
+     *               {@link ImageFormat#NV21}. The supported multi-resolution reader format can be
      *               queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}.
      *
      * @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader.
      *
-     * @throws IllegaArgumentException If the {@code streams} is null or doesn't contain
+     * @throws IllegalArgumentException If the {@code streams} is null or doesn't contain
      *                                 at least 2 items, or if {@code format} isn't a valid camera
      *                                 format.
      *
@@ -710,7 +712,7 @@
      * instances.</p>
      *
      * @param outputConfigurations The OutputConfiguration objects created by {@link
-     *                             #createInstancesFromMultiResolutionOutput}
+     *                             #createInstancesForMultiResolutionOutput}
      * @param multiResolutionImageReader The MultiResolutionImageReader object created from the same
      *                                   MultiResolutionStreamInfo parameters as
      *                                   {@code outputConfigurations}.
@@ -759,31 +761,33 @@
      * the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView}
      * by calling {@link android.view.SurfaceHolder#getSurface}, (2) from
      * {@link android.graphics.SurfaceTexture} via
-     * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link
-     * android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link
-     * android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link
-     * android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link
-     * android.media.MediaCodec#createPersistentInputSource}.</p>
+     * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from
+     * {@link android.media.MediaRecorder} via {@link android.media.MediaRecorder#getSurface} or
+     * {@link android.media.MediaCodec#createPersistentInputSurface}, or (4) from
+     * {@link android.media.MediaCodec} via {@link android.media.MediaCodec#createInputSurface} or
+     * {@link android.media.MediaCodec#createPersistentInputSurface}.</p>
      *
      * <ul>
      * <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture}
      * can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the
      * output Surface must be set via {@link #addSurface}, and the Surface configuration must be
-     * finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting
+     * finalized via {@link CameraCaptureSession#finalizeOutputConfigurations} before submitting
      * a request with the Surface target.</li>
      * <li>For all other target types, the output Surface must be set by {@link #addSurface},
-     * and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the
+     * and {@link CameraCaptureSession#finalizeOutputConfigurations} is not needed because the
      * OutputConfiguration used to create the session will contain the actual Surface.</li>
      * </ul>
      *
      * <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link
      * android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both
      * kind of outputs can be deferred until after {@link
-     * CameraDevice#createCaptureSessionByOutputConfiguration}.</p>
+     * CameraDevice#createCaptureSessionByOutputConfigurations}.</p>
      *
      * <p>An OutputConfiguration object created by this constructor can be used for {@link
-     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
-     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+     * and {@link
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+     * having called {@link #addSurface}.</p>
      *
      * @param surfaceSize Size for the deferred surface.
      * @param klass a non-{@code null} {@link Class} object reference that indicates the source of
@@ -849,8 +853,10 @@
      * before creating the capture session.</p>
      *
      * <p>An OutputConfiguration object created by this constructor can be used for {@link
-     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
-     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+     * and {@link
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+     * having called {@link #addSurface}.</p>
      *
      * @param format The format of the ImageReader output. This must be one of the
      *               {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
@@ -873,8 +879,10 @@
      * before creating the capture session.</p>
      *
      * <p>An OutputConfiguration object created by this constructor can be used for {@link
-     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
-     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+     * and {@link
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+     * having called {@link #addSurface}.</p>
      *
      * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
      *                       outputs.
@@ -899,8 +907,10 @@
      * before creating the capture session.</p>
      *
      * <p>An OutputConfiguration object created by this constructor can be used for {@link
-     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
-     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+     * and {@link
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+     * having called {@link #addSurface}.</p>
      *
      * @param format The format of the ImageReader output. This must be one of the
      *               {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
@@ -923,8 +933,10 @@
      * before creating the capture session.</p>
      *
      * <p>An OutputConfiguration object created by this constructor can be used for {@link
-     * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
-     * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported}
+     * and {@link
+     * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without
+     * having called {@link #addSurface}.</p>
      *
      * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
      *                       outputs.
@@ -1171,9 +1183,9 @@
      * <li>from {@link android.media.MediaRecorder} by calling
      * {@link android.media.MediaRecorder#getSurface} or {@link
      * android.media.MediaCodec#createPersistentInputSurface}</li>
-     * <li>from {@link android.media.MediaCodce} by calling
-     * {@link android.media.MediaCodec#createInputSurface} or {@link
-     * android.media.MediaCodec#createPersistentInputSource}</li>
+     * <li>from {@link android.media.MediaCodec} by calling
+     * {@link android.media.MediaCodec#createInputSurface} or
+     * {@link android.media.MediaCodec#createPersistentInputSurface()}</li>
      * </ul>
      *
      * <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 3d7b714..8519722 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -548,6 +548,20 @@
         }
     }
 
+    /**
+     * Request to power a display ON or OFF.
+     * @hide
+     */
+    @RequiresPermission("android.permission.MANAGE_DISPLAYS")
+    public boolean requestDisplayPower(int displayId, boolean on) {
+        try {
+            return mDm.requestDisplayPower(displayId, on);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "Error trying to request display power " + on, ex);
+            return false;
+        }
+    }
+
     public void startWifiDisplayScan() {
         synchronized (mLock) {
             if (mWifiDisplayScanNestCount++ == 0) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 70efc6f..b7c02b0 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -236,6 +236,10 @@
     @EnforcePermission("MANAGE_DISPLAYS")
     void disableConnectedDisplay(int displayId);
 
+    // Request to power display ON or OFF.
+    @EnforcePermission("MANAGE_DISPLAYS")
+    boolean requestDisplayPower(int displayId, boolean on);
+
     // Restricts display modes to specified modeIds.
     @EnforcePermission("RESTRICT_DISPLAY_MODES")
     void requestDisplayModes(in IBinder token, int displayId, in @nullable int[] modeIds);
diff --git a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
index e02169e..992dbff 100644
--- a/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
+++ b/core/java/android/hardware/location/ISignificantPlaceProvider.aidl
@@ -7,4 +7,5 @@
  */
 oneway interface ISignificantPlaceProvider {
     void setSignificantPlaceProviderManager(in ISignificantPlaceProviderManager manager);
+    void onSignificantPlaceCheck();
 }
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 967fc42..0f944cf 100644
--- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -28,4 +28,11 @@
     namespace: "preload_safety"
     description: "Enables signal API with staking"
     bug: "287498482"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "enable_usb_sysfs_midi_identification"
+    namespace: "system_sw_usb"
+    description: "Enable identifying midi device using USB sysfs"
+    bug: "333778731"
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 278e863..943b04f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -401,10 +401,7 @@
     private long mStylusHwSessionsTimeout = STYLUS_HANDWRITING_IDLE_TIMEOUT_MS;
     private Runnable mStylusWindowIdleTimeoutRunnable;
     private long mStylusWindowIdleTimeoutForTest;
-    /**
-     * Tracks last {@link MotionEvent#getToolType(int)} used for {@link MotionEvent#ACTION_DOWN}.
-     **/
-    private int mLastUsedToolType;
+
     /**
      * Tracks the ctrl+shift shortcut
      **/
@@ -720,6 +717,7 @@
 
     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
         onComputeInsets(mTmpInsets);
+        mNavigationBarController.updateInsets(mTmpInsets);
         if (!mViewsCreated) {
             // The IME views are not ready, keep visible insets untouched.
             mTmpInsets.visibleTopInsets = 0;
@@ -1367,7 +1365,6 @@
 
     private void updateEditorToolTypeInternal(int toolType) {
         if (Flags.useHandwritingListenerForTooltype()) {
-            mLastUsedToolType = toolType;
             if (mInputEditorInfo != null) {
                 mInputEditorInfo.setInitialToolType(toolType);
             }
@@ -3384,9 +3381,6 @@
                 null /* icProto */);
         mInputStarted = true;
         mStartedInputConnection = ic;
-        if (Flags.useHandwritingListenerForTooltype()) {
-            editorInfo.setInitialToolType(mLastUsedToolType);
-        }
         mInputEditorInfo = editorInfo;
         initialize();
         mInlineSuggestionSessionController.notifyOnStartInput(
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index 9c55b0e..de67e06 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -58,6 +58,10 @@
 final class NavigationBarController {
 
     private interface Callback {
+
+        default void updateInsets(@NonNull InputMethodService.Insets originalInsets) {
+        }
+
         default void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
                 @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
         }
@@ -96,6 +100,15 @@
                 ? new Impl(inputMethodService) : Callback.NOOP;
     }
 
+    /**
+     * Update the given insets to be at least as big as the IME navigation bar, when visible.
+     *
+     * @param originalInsets the insets to check and modify to include the IME navigation bar.
+     */
+    void updateInsets(@NonNull InputMethodService.Insets originalInsets) {
+        mImpl.updateInsets(originalInsets);
+    }
+
     void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
             @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
         mImpl.updateTouchableInsets(originalInsets, dest);
@@ -270,6 +283,24 @@
         }
 
         @Override
+        public void updateInsets(@NonNull InputMethodService.Insets originalInsets) {
+            if (!mImeDrawsImeNavBar || mNavigationBarFrame == null
+                    || mNavigationBarFrame.getVisibility() != View.VISIBLE
+                    || mService.isFullscreenMode()) {
+                return;
+            }
+
+            final int[] loc = new int[2];
+            mNavigationBarFrame.getLocationInWindow(loc);
+            if (originalInsets.contentTopInsets > loc[1]) {
+                originalInsets.contentTopInsets = loc[1];
+            }
+            if (originalInsets.visibleTopInsets > loc[1]) {
+                originalInsets.visibleTopInsets = loc[1];
+            }
+        }
+
+        @Override
         public void updateTouchableInsets(@NonNull InputMethodService.Insets originalInsets,
                 @NonNull ViewTreeObserver.InternalInsetsInfo dest) {
             if (!mImeDrawsImeNavBar || mNavigationBarFrame == null) {
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index b417534..02f3a25 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -197,6 +197,11 @@
             POWER_COMPONENT_MOBILE_RADIO,
             POWER_COMPONENT_WIFI,
             POWER_COMPONENT_BLUETOOTH,
+            POWER_COMPONENT_AUDIO,
+            POWER_COMPONENT_VIDEO,
+            POWER_COMPONENT_FLASHLIGHT,
+            POWER_COMPONENT_CAMERA,
+            POWER_COMPONENT_GNSS,
     };
 
     static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 3e6223a..065b3d6 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1994,7 +1994,8 @@
 
         // STATES2 bits that are used for Power Stats tracking
         public static final int IMPORTANT_FOR_POWER_STATS_STATES2 =
-                STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG;
+                STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG
+                | STATE2_GPS_SIGNAL_QUALITY_MASK;
 
         @UnsupportedAppUsage
         public int states2;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
old mode 100755
new mode 100644
index 8aa2c35..30d2dec
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1238,6 +1238,18 @@
         public static final int VANILLA_ICE_CREAM = 35;
     }
 
+    /**
+     * The vendor API for 2024 Q2
+     *
+     * <p>For Android 14-QPR3 and later, the vendor API level is completely decoupled from the SDK
+     * API level and the format has switched to YYYYMM (year and month)
+     *
+     * @see <a href="https://preview.source.android.com/docs/core/architecture/api-flags">Vendor API
+     *     level</a>
+     * @hide
+     */
+    public static final int VENDOR_API_2024_Q2 = 202404;
+
     /** The type of build, like "user" or "eng". */
     public static final String TYPE = getString("ro.build.type");
 
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 61b52c6..e6b1c07 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -580,6 +580,8 @@
                            ", copied:" + progress +
                            ", read:" + (count - countToRead) +
                            ", in pipe: " + countInPipe);
+                    Os.close(pipes[0]);
+                    Os.close(pipes[1]);
                     throw new ErrnoException("splice, pipe --> fdOut", EIO);
                 } else {
                     progress += t;
@@ -607,6 +609,8 @@
                 listener.onProgress(progressSnapshot);
             });
         }
+        Os.close(pipes[0]);
+        Os.close(pipes[1]);
         return progress;
     }
 
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 04d4970..6d6757d5 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -3,9 +3,9 @@
 per-file *Vibrator* = file:/services/core/java/com/android/server/vibrator/OWNERS
 
 # PowerManager
-per-file IPowerManager.aidl = michaelwr@google.com, santoscordon@google.com
-per-file PowerManager.java = michaelwr@google.com, santoscordon@google.com
-per-file PowerManagerInternal.java = michaelwr@google.com, santoscordon@google.com
+per-file IPowerManager.aidl = file:/services/core/java/com/android/server/power/OWNERS
+per-file PowerManager.java = file:/services/core/java/com/android/server/power/OWNERS
+per-file PowerManagerInternal.java = file:/services/core/java/com/android/server/power/OWNERS
 
 # BatteryStats
 per-file *BatteryConsumer* = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index fdce476..7926afe 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1907,6 +1907,31 @@
             "no_near_field_communication_radio";
 
     /**
+     * This user restriction specifies if Near-field communication is disallowed to change
+     * on the device. If Near-field communication is disallowed it cannot be changed via Settings.
+     *
+     * <p>This restriction can only be set by a device owner or a profile owner of an
+     * organization-owned managed profile on the parent profile.
+     * In both cases, the restriction applies globally on the device and will not allow Near-field
+     * communication state being changed.
+     *
+     * <p>
+     * Near-field communication (NFC) is a radio technology that allows two devices (like your phone
+     * and a payments terminal) to communicate with each other when they're close together.
+     *
+     * <p>Default is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_USER_RESTRICTION)
+    public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO =
+            "no_change_near_field_communication_radio";
+
+    /**
      * This user restriction specifies if Thread network is disallowed on the device. If Thread
      * network is disallowed it cannot be turned on via Settings.
      *
@@ -2007,6 +2032,7 @@
             DISALLOW_CAMERA,
             DISALLOW_CAMERA_TOGGLE,
             DISALLOW_CELLULAR_2G,
+            DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO,
             DISALLOW_CHANGE_WIFI_STATE,
             DISALLOW_CONFIG_BLUETOOTH,
             DISALLOW_CONFIG_BRIGHTNESS,
@@ -5551,8 +5577,8 @@
     }
 
     /**
-     * Enables or disables quiet mode for a managed profile. If quiet mode is enabled, apps in a
-     * managed profile don't run, generate notifications, or consume data or battery.
+     * Enables or disables quiet mode for a profile. If quiet mode is enabled, apps in the profile
+     * don't run, generate notifications, or consume data or battery.
      * <p>
      * If a user's credential is needed to turn off quiet mode, a confirm credential screen will be
      * shown to the user.
@@ -5560,8 +5586,11 @@
      * The change may not happen instantly, however apps can listen for
      * {@link Intent#ACTION_MANAGED_PROFILE_AVAILABLE} and
      * {@link Intent#ACTION_MANAGED_PROFILE_UNAVAILABLE} broadcasts in order to be notified of
-     * the change of the quiet mode. Apps can also check the current state of quiet mode by
-     * calling {@link #isQuietModeEnabled(UserHandle)}.
+     * the change of the quiet mode for managed profile.
+     * Apps can listen to generic broadcasts {@link Intent#ACTION_PROFILE_AVAILABLE} and
+     * {@link Intent#ACTION_PROFILE_UNAVAILABLE} to be notified of the change in quiet mode for
+     * any profiles. Apps can also check the current state of quiet mode by calling
+     * {@link #isQuietModeEnabled(UserHandle)}.
      * <p>
      * The caller must either be the foreground default launcher or have one of these permissions:
      * {@code MANAGE_USERS} or {@code MODIFY_QUIET_MODE}.
@@ -5571,7 +5600,7 @@
      * @return {@code false} if user's credential is needed in order to turn off quiet mode,
      *         {@code true} otherwise
      * @throws SecurityException if the caller is invalid
-     * @throws IllegalArgumentException if {@code userHandle} is not a managed profile
+     * @throws IllegalArgumentException if {@code userHandle} is not a profile
      *
      * @see #isQuietModeEnabled(UserHandle)
      */
@@ -5636,7 +5665,6 @@
 
     /**
      * Returns whether the given profile is in quiet mode or not.
-     * Notes: Quiet mode is only supported for managed profiles.
      *
      * @param userHandle The user handle of the profile to be queried.
      * @return true if the profile is in quiet mode, false otherwise.
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 34fb963..e029e52 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -193,7 +193,7 @@
     namespace: "permissions"
     description: "Enable getDeviceId API in OpEventProxyInfo"
     bug: "337340961"
- }
+}
 
 flag {
     name: "device_aware_app_op_new_schema_enabled"
@@ -201,4 +201,15 @@
     namespace: "permissions"
     description: "Persist device attributed AppOp accesses on the disk"
     bug: "308201969"
-}
\ No newline at end of file
+}
+
+flag {
+    name: "apex_signature_permission_allowlist_enabled"
+    is_fixed_read_only: true
+    namespace: "permissions"
+    description: "Enable reading signature permission allowlist from APEXes"
+    bug: "308573169"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 53a30b2..d91508f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6101,6 +6101,15 @@
         public static final String POINTER_SPEED = "pointer_speed";
 
         /**
+         * Pointer scale setting.
+         *
+         * <p>This float value represents the scale by which the size of the pointer increases.
+         * @hide
+         */
+        @Readable
+        public static final String POINTER_SCALE = "pointer_scale";
+
+        /**
          * Touchpad pointer speed setting.
          * This is an integer value in a range between -7 and +7, so there are 15 possible values.
          *   -7 = slowest
@@ -6147,6 +6156,15 @@
         public static final String TOUCHPAD_RIGHT_CLICK_ZONE = "touchpad_right_click_zone";
 
         /**
+         * Pointer fill style, specified by
+         * {@link android.view.PointerIcon.PointerIconVectorStyleFill} constants.
+         *
+         * @hide
+         */
+        @Readable
+        public static final String POINTER_FILL_STYLE = "pointer_fill_style";
+
+        /**
          * Whether lock-to-app will be triggered by long-press on recents.
          * @hide
          */
@@ -6348,6 +6366,8 @@
             PRIVATE_SETTINGS.add(SIP_ADDRESS_ONLY);
             PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME);
             PRIVATE_SETTINGS.add(POINTER_SPEED);
+            PRIVATE_SETTINGS.add(POINTER_FILL_STYLE);
+            PRIVATE_SETTINGS.add(POINTER_SCALE);
             PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED);
             PRIVATE_SETTINGS.add(EGG_MODE);
             PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
@@ -12684,6 +12704,16 @@
          * @hide
          */
         public static final String CHARGE_OPTIMIZATION_MODE = "charge_optimization_mode";
+
+        /**
+         * String property which contains the package name of the contextual
+         * search provider supplied by individual OEM's
+         * R.string.config_defaultContextualSearchPackageName.
+         *
+         * @hide
+         */
+        @Readable
+        public static final String CONTEXTUAL_SEARCH_PACKAGE = "contextual_search_package";
     }
 
     /**
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index e1965ef..405fe26 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -29,6 +29,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.app.assist.AssistContent;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
 import android.content.Intent;
@@ -133,6 +134,16 @@
      */
     public static final String SERVICE_META_DATA = "android.content_capture";
 
+
+    /**
+     * Extras key to flag that the passed in {@link AssistContent} is sent only during Activity
+     * start.
+     *
+     * @hide
+     */
+    public static final String ASSIST_CONTENT_ACTIVITY_START_KEY = "activity_start_assist_content";
+
+
     private final LocalDataShareAdapterResourceManager mDataShareAdapterResourceManager =
             new LocalDataShareAdapterResourceManager();
 
diff --git a/core/java/android/service/dreams/OWNERS b/core/java/android/service/dreams/OWNERS
index 77bcee8..119ca55 100644
--- a/core/java/android/service/dreams/OWNERS
+++ b/core/java/android/service/dreams/OWNERS
@@ -1,10 +1,11 @@
-# Bug component: 78010
+# Bug component: 66910
 
 brycelee@google.com
-dsandler@google.com
-galinap@google.com
-jjaggi@google.com
 lusilva@google.com
-michaelwr@google.com
-santoscordon@google.com
 wxyz@google.com
+justinkoh@google.com
+
+rgl@google.com
+santoscordon@google.com
+
+dsandler@google.com
diff --git a/core/java/android/service/notification/ZenAdapters.java b/core/java/android/service/notification/ZenAdapters.java
index b249815..a122b71 100644
--- a/core/java/android/service/notification/ZenAdapters.java
+++ b/core/java/android/service/notification/ZenAdapters.java
@@ -33,7 +33,7 @@
                 .allowAlarms(policy.allowAlarms())
                 .allowCalls(
                         policy.allowCalls()
-                                ? notificationPolicySendersToZenPolicyPeopleType(
+                                ? prioritySendersToPeopleType(
                                         policy.allowCallsFrom())
                         : ZenPolicy.PEOPLE_TYPE_NONE)
                 .allowConversations(
@@ -45,7 +45,7 @@
                 .allowMedia(policy.allowMedia())
                 .allowMessages(
                         policy.allowMessages()
-                                ? notificationPolicySendersToZenPolicyPeopleType(
+                                ? prioritySendersToPeopleType(
                                         policy.allowMessagesFrom())
                                 : ZenPolicy.PEOPLE_TYPE_NONE)
                 .allowReminders(policy.allowReminders())
@@ -71,7 +71,7 @@
 
     /** Maps {@link ZenPolicy.PeopleType} enum to {@link Policy.PrioritySenders}. */
     @Policy.PrioritySenders
-    public static int zenPolicyPeopleTypeToNotificationPolicySenders(
+    public static int peopleTypeToPrioritySenders(
             @ZenPolicy.PeopleType int zpPeopleType, @Policy.PrioritySenders int defaultResult) {
         switch (zpPeopleType) {
             case ZenPolicy.PEOPLE_TYPE_ANYONE:
@@ -87,7 +87,7 @@
 
     /** Maps {@link Policy.PrioritySenders} enum to {@link ZenPolicy.PeopleType}. */
     @ZenPolicy.PeopleType
-    public static int notificationPolicySendersToZenPolicyPeopleType(
+    public static int prioritySendersToPeopleType(
             @Policy.PrioritySenders int npPrioritySenders) {
         switch (npPrioritySenders) {
             case Policy.PRIORITY_SENDERS_ANY:
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/core/java/android/service/notification/ZenDeviceEffects.aidl
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
copy to core/java/android/service/notification/ZenDeviceEffects.aidl
index 23dbc26..e635493 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
+++ b/core/java/android/service/notification/ZenDeviceEffects.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2024 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.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recordissue
+package android.service.notification;
 
-import com.android.traceur.TraceUtils.PresetTraceType
+parcelable ZenDeviceEffects;
 
-data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 610a317..7a0c016 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -24,9 +24,26 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
-import static android.service.notification.ZenAdapters.notificationPolicySendersToZenPolicyPeopleType;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
+import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
 import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy;
-import static android.service.notification.ZenAdapters.zenPolicyPeopleTypeToNotificationPolicySenders;
+import static android.service.notification.ZenModeConfig.EventInfo.REPLY_YES_OR_MAYBE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
+import static android.service.notification.ZenPolicy.STATE_ALLOW;
+import static android.service.notification.ZenPolicy.STATE_DISALLOW;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
 
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
@@ -58,6 +75,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
@@ -320,31 +338,107 @@
 
     @UnsupportedAppUsage
     public ZenModeConfig() {
+        if (Flags.modesUi()) {
+            ensureManualZenRule();
+        }
     }
 
     public ZenModeConfig(Parcel source) {
-        allowCalls = source.readInt() == 1;
-        allowRepeatCallers = source.readInt() == 1;
-        allowMessages = source.readInt() == 1;
-        allowReminders = source.readInt() == 1;
-        allowEvents = source.readInt() == 1;
-        allowCallsFrom = source.readInt();
-        allowMessagesFrom = source.readInt();
+        if (!Flags.modesUi()) {
+            allowCalls = source.readInt() == 1;
+            allowRepeatCallers = source.readInt() == 1;
+            allowMessages = source.readInt() == 1;
+            allowReminders = source.readInt() == 1;
+            allowEvents = source.readInt() == 1;
+            allowCallsFrom = source.readInt();
+            allowMessagesFrom = source.readInt();
+        }
         user = source.readInt();
         manualRule = source.readParcelable(null, ZenRule.class);
         readRulesFromParcel(automaticRules, source);
         if (Flags.modesApi()) {
             readRulesFromParcel(deletedRules, source);
         }
-        allowAlarms = source.readInt() == 1;
-        allowMedia = source.readInt() == 1;
-        allowSystem = source.readInt() == 1;
-        suppressedVisualEffects = source.readInt();
+        if (!Flags.modesUi()) {
+            allowAlarms = source.readInt() == 1;
+            allowMedia = source.readInt() == 1;
+            allowSystem = source.readInt() == 1;
+            suppressedVisualEffects = source.readInt();
+        }
         areChannelsBypassingDnd = source.readInt() == 1;
-        allowConversations = source.readBoolean();
-        allowConversationsFrom = source.readInt();
-        if (Flags.modesApi()) {
-            allowPriorityChannels = source.readBoolean();
+        if (!Flags.modesUi()) {
+            allowConversations = source.readBoolean();
+            allowConversationsFrom = source.readInt();
+            if (Flags.modesApi()) {
+                allowPriorityChannels = source.readBoolean();
+            }
+        }
+    }
+
+    public static ZenPolicy getDefaultZenPolicy() {
+        ZenPolicy policy = new ZenPolicy.Builder()
+                .allowAlarms(true)
+                .allowMedia(true)
+                .allowSystem(false)
+                .allowCalls(PEOPLE_TYPE_STARRED)
+                .allowMessages(PEOPLE_TYPE_STARRED)
+                .allowReminders(false)
+                .allowEvents(false)
+                .allowRepeatCallers(true)
+                .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+                .showAllVisualEffects()
+                .showVisualEffect(VISUAL_EFFECT_FULL_SCREEN_INTENT, false)
+                .showVisualEffect(VISUAL_EFFECT_LIGHTS, false)
+                .showVisualEffect(VISUAL_EFFECT_PEEK, false)
+                .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
+                .allowPriorityChannels(true)
+                .build();
+        return policy;
+    }
+
+    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;
+        scheduleInfo.endHour = 7;
+        scheduleInfo.exitAtAlarm = true;
+        ZenRule sleeping = new ZenRule();
+        sleeping.id = EVERY_NIGHT_DEFAULT_RULE_ID;
+        sleeping.conditionId = toScheduleConditionId(scheduleInfo);
+        sleeping.component = ComponentName.unflattenFromString(
+                "android/com.android.server.notification.ScheduleConditionProvider");
+        sleeping.enabled = false;
+        sleeping.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        sleeping.pkg = "android";
+        config.automaticRules.put(EVERY_NIGHT_DEFAULT_RULE_ID, sleeping);
+
+        return config;
+    }
+
+    void ensureManualZenRule() {
+        if (manualRule == null) {
+            final ZenRule newRule = new ZenRule();
+            newRule.type = AutomaticZenRule.TYPE_OTHER;
+            newRule.enabled = true;
+            newRule.conditionId = Uri.EMPTY;
+            newRule.allowManualInvocation = true;
+            newRule.zenPolicy = getDefaultZenPolicy();
+            newRule.pkg = "android";
+            manualRule = newRule;
         }
     }
 
@@ -363,28 +457,34 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(allowCalls ? 1 : 0);
-        dest.writeInt(allowRepeatCallers ? 1 : 0);
-        dest.writeInt(allowMessages ? 1 : 0);
-        dest.writeInt(allowReminders ? 1 : 0);
-        dest.writeInt(allowEvents ? 1 : 0);
-        dest.writeInt(allowCallsFrom);
-        dest.writeInt(allowMessagesFrom);
+        if (!Flags.modesUi()) {
+            dest.writeInt(allowCalls ? 1 : 0);
+            dest.writeInt(allowRepeatCallers ? 1 : 0);
+            dest.writeInt(allowMessages ? 1 : 0);
+            dest.writeInt(allowReminders ? 1 : 0);
+            dest.writeInt(allowEvents ? 1 : 0);
+            dest.writeInt(allowCallsFrom);
+            dest.writeInt(allowMessagesFrom);
+        }
         dest.writeInt(user);
         dest.writeParcelable(manualRule, 0);
         writeRulesToParcel(automaticRules, dest);
         if (Flags.modesApi()) {
             writeRulesToParcel(deletedRules, dest);
         }
-        dest.writeInt(allowAlarms ? 1 : 0);
-        dest.writeInt(allowMedia ? 1 : 0);
-        dest.writeInt(allowSystem ? 1 : 0);
-        dest.writeInt(suppressedVisualEffects);
+        if (!Flags.modesUi()) {
+            dest.writeInt(allowAlarms ? 1 : 0);
+            dest.writeInt(allowMedia ? 1 : 0);
+            dest.writeInt(allowSystem ? 1 : 0);
+            dest.writeInt(suppressedVisualEffects);
+        }
         dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
-        dest.writeBoolean(allowConversations);
-        dest.writeInt(allowConversationsFrom);
-        if (Flags.modesApi()) {
-            dest.writeBoolean(allowPriorityChannels);
+        if (!Flags.modesUi()) {
+            dest.writeBoolean(allowConversations);
+            dest.writeInt(allowConversationsFrom);
+            if (Flags.modesApi()) {
+                dest.writeBoolean(allowPriorityChannels);
+            }
         }
     }
 
@@ -408,35 +508,251 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
-                .append("user=").append(user)
-                .append(",allowAlarms=").append(allowAlarms)
-                .append(",allowMedia=").append(allowMedia)
-                .append(",allowSystem=").append(allowSystem)
-                .append(",allowReminders=").append(allowReminders)
-                .append(",allowEvents=").append(allowEvents)
-                .append(",allowCalls=").append(allowCalls)
-                .append(",allowRepeatCallers=").append(allowRepeatCallers)
-                .append(",allowMessages=").append(allowMessages)
-                .append(",allowConversations=").append(allowConversations)
-                .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
-                .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
-                .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString
-                        (allowConversationsFrom))
-                .append(",suppressedVisualEffects=").append(suppressedVisualEffects);
+                .append("user=").append(user);
+        if (!Flags.modesUi()) {
+            sb.append(",allowAlarms=").append(allowAlarms)
+                    .append(",allowMedia=").append(allowMedia)
+                    .append(",allowSystem=").append(allowSystem)
+                    .append(",allowReminders=").append(allowReminders)
+                    .append(",allowEvents=").append(allowEvents)
+                    .append(",allowCalls=").append(allowCalls)
+                    .append(",allowRepeatCallers=").append(allowRepeatCallers)
+                    .append(",allowMessages=").append(allowMessages)
+                    .append(",allowConversations=").append(allowConversations)
+                    .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
+                    .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
+                    .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString
+                            (allowConversationsFrom))
+                    .append("\nsuppressedVisualEffects=").append(suppressedVisualEffects);
+        }
         if (Flags.modesApi()) {
-            sb.append(",hasPriorityChannels=").append(areChannelsBypassingDnd);
+            sb.append("\nhasPriorityChannels=").append(areChannelsBypassingDnd);
             sb.append(",allowPriorityChannels=").append(allowPriorityChannels);
         } else {
-            sb.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd);
+            sb.append("\nareChannelsBypassingDnd=").append(areChannelsBypassingDnd);
         }
-        sb.append(",\nautomaticRules=").append(rulesToString(automaticRules))
-                .append(",\nmanualRule=").append(manualRule);
+        sb.append(",\nautomaticRules=").append(rulesToString(automaticRules));
+        sb.append(",\nmanualRule=").append(manualRule);
         if (Flags.modesApi()) {
             sb.append(",\ndeletedRules=").append(rulesToString(deletedRules));
         }
         return sb.append(']').toString();
     }
 
+    public boolean isAllowPriorityChannels() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowPriorityChannels;
+    }
+
+    public void setAllowPriorityChannels(boolean allowPriorityChannels) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowPriorityChannels = allowPriorityChannels;
+        }
+    }
+
+    public int getSuppressedVisualEffects() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            return this.suppressedVisualEffects;
+        }
+    }
+
+    public void setSuppressedVisualEffects(int suppressedVisualEffects) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.suppressedVisualEffects = suppressedVisualEffects;
+        }
+    }
+
+    public @ZenPolicy.ConversationSenders int getAllowConversationsFrom() {
+        if (Flags.modesUi()) {
+            return manualRule.zenPolicy.getPriorityConversationSenders();
+        }
+        return allowConversationsFrom;
+    }
+
+    public void setAllowConversationsFrom(
+            @ZenPolicy.ConversationSenders int allowConversationsFrom) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowConversationsFrom = allowConversationsFrom;
+        }
+    }
+
+    public void setAllowConversations(boolean allowConversations) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowConversations = allowConversations;
+        }
+    }
+
+    public boolean isAllowConversations() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowConversations;
+    }
+
+    public @Policy.PrioritySenders int getAllowMessagesFrom() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowMessagesFrom;
+    }
+
+    public void setAllowMessagesFrom(@Policy.PrioritySenders int allowMessagesFrom) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowMessagesFrom = allowMessagesFrom;
+        }
+    }
+
+    public void setAllowMessages(boolean allowMessages) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        this.allowMessages = allowMessages;
+    }
+
+    public @Policy.PrioritySenders int getAllowCallsFrom() {
+        if (Flags.modesUi()) {
+            return peopleTypeToPrioritySenders(
+                    manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE);
+        }
+        return allowCallsFrom;
+    }
+
+    public void setAllowCallsFrom(@Policy.PrioritySenders int allowCallsFrom) {
+        if (Flags.modesUi()) {
+            manualRule.zenPolicy = new ZenPolicy.Builder(manualRule.zenPolicy)
+                    .allowCalls(prioritySendersToPeopleType(allowCallsFrom))
+                    .build();
+        } else {
+            this.allowCallsFrom = allowCallsFrom;
+        }
+    }
+
+    public void setAllowCalls(boolean allowCalls) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        this.allowCalls = allowCalls;
+    }
+
+    public boolean isAllowEvents() {
+        if (Flags.modesUi()) {
+            return manualRule.zenPolicy.isCategoryAllowed(
+                    ZenPolicy.PRIORITY_CATEGORY_EVENTS, false);
+        }
+        return allowEvents;
+    }
+
+    public void setAllowEvents(boolean allowEvents) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowEvents = allowEvents;
+        }
+    }
+
+    public boolean isAllowReminders() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowReminders;
+    }
+
+    public void setAllowReminders(boolean allowReminders) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowReminders = allowReminders;
+        }
+    }
+
+    public boolean isAllowMessages() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowMessages;
+    }
+
+    public boolean isAllowRepeatCallers() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowRepeatCallers;
+    }
+
+    public void setAllowRepeatCallers(boolean allowRepeatCallers) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowRepeatCallers = allowRepeatCallers;
+        }
+    }
+
+    public boolean isAllowSystem() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowSystem;
+    }
+
+    public void setAllowSystem(boolean allowSystem) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowSystem = allowSystem;
+        }
+    }
+
+    public boolean isAllowMedia() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowMedia;
+    }
+
+    public void setAllowMedia(boolean allowMedia) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowMedia = allowMedia;
+        }
+    }
+
+    public boolean isAllowAlarms() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowAlarms;
+    }
+
+    public void setAllowAlarms(boolean allowAlarms) {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        } else {
+            this.allowAlarms = allowAlarms;
+        }
+    }
+
+    public boolean isAllowCalls() {
+        if (Flags.modesUi()) {
+            throw new IllegalStateException("can't be used with modesUI flag");
+        }
+        return allowCalls;
+    }
+
     private static String rulesToString(ArrayMap<String, ZenRule> ruleList) {
         if (ruleList.isEmpty()) {
             return "{}";
@@ -512,6 +828,8 @@
         if (!(o instanceof ZenModeConfig)) return false;
         if (o == this) return true;
         final ZenModeConfig other = (ZenModeConfig) o;
+        // The policy fields that live on config are compared directly because the fields will
+        // contain data until MODES_UI is rolled out/cleaned up.
         boolean eq = other.allowAlarms == allowAlarms
                 && other.allowMedia == allowMedia
                 && other.allowSystem == allowSystem
@@ -539,6 +857,8 @@
 
     @Override
     public int hashCode() {
+        // The policy fields that live on config are compared directly because the fields will
+        // contain data until MODES_UI is rolled out/cleaned up.
         if (Flags.modesApi()) {
             return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
                     allowRepeatCallers, allowMessages,
@@ -619,9 +939,14 @@
         rt.version = safeInt(parser, ZEN_ATT_VERSION, getCurrentXmlVersion());
         rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
         boolean readSuppressedEffects = false;
+        boolean readManualRule = false;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
             tag = parser.getName();
             if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
+                if (Flags.modesUi() && !readManualRule) {
+                    // migrate from fields on config into manual rule
+                    rt.manualRule.zenPolicy = rt.toZenPolicy();
+                }
                 return rt;
             }
             if (type == XmlPullParser.START_TAG) {
@@ -693,6 +1018,9 @@
                             DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
                 } else if (MANUAL_TAG.equals(tag)) {
                     rt.manualRule = readRuleXml(parser);
+                    if (rt.manualRule != null) {
+                        readManualRule = true;
+                    }
                 } else if (AUTOMATIC_TAG.equals(tag)
                         || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
                     final String id = parser.getAttributeValue(null, RULE_ATT_ID);
@@ -742,6 +1070,9 @@
                 ? Integer.toString(xmlVersion) : Integer.toString(version));
         out.attributeInt(null, ZEN_ATT_USER, user);
         out.startTag(null, ALLOW_TAG);
+        // From MODES_UI these fields are only read if the flag has transitioned from off to on
+        // However, we will continue to write these fields until the flag is cleaned up so it's
+        // possible to turn the flag off without losing user data
         out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls);
         out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers);
         out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages);
@@ -816,11 +1147,11 @@
         rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
         rt.condition = readConditionXml(parser);
 
-        if (!Flags.modesApi() && rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+        if (!Flags.modesApi() && rt.zenMode != ZEN_MODE_IMPORTANT_INTERRUPTIONS
                 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
             // all default rules and user created rules updated to zenMode important interruptions
             Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
-            rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+            rt.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         }
         rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
         rt.zenPolicy = readZenPolicyXml(parser);
@@ -952,7 +1283,7 @@
         if (Flags.modesApi()) {
             final int channels = safeInt(parser, ALLOW_ATT_CHANNELS, ZenPolicy.STATE_UNSET);
             if (channels != ZenPolicy.STATE_UNSET) {
-                builder.allowPriorityChannels(channels == ZenPolicy.STATE_ALLOW);
+                builder.allowPriorityChannels(channels == STATE_ALLOW);
                 policySet = true;
             }
         }
@@ -966,7 +1297,7 @@
             policySet = true;
         }
         if (repeatCallers != ZenPolicy.STATE_UNSET) {
-            builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW);
+            builder.allowRepeatCallers(repeatCallers == STATE_ALLOW);
             policySet = true;
         }
         if (conversations != ZenPolicy.CONVERSATION_SENDERS_UNSET) {
@@ -974,23 +1305,23 @@
             policySet = true;
         }
         if (alarms != ZenPolicy.STATE_UNSET) {
-            builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW);
+            builder.allowAlarms(alarms == STATE_ALLOW);
             policySet = true;
         }
         if (media != ZenPolicy.STATE_UNSET) {
-            builder.allowMedia(media == ZenPolicy.STATE_ALLOW);
+            builder.allowMedia(media == STATE_ALLOW);
             policySet = true;
         }
         if (system != ZenPolicy.STATE_UNSET) {
-            builder.allowSystem(system == ZenPolicy.STATE_ALLOW);
+            builder.allowSystem(system == STATE_ALLOW);
             policySet = true;
         }
         if (events != ZenPolicy.STATE_UNSET) {
-            builder.allowEvents(events == ZenPolicy.STATE_ALLOW);
+            builder.allowEvents(events == STATE_ALLOW);
             policySet = true;
         }
         if (reminders != ZenPolicy.STATE_UNSET) {
-            builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW);
+            builder.allowReminders(reminders == STATE_ALLOW);
             policySet = true;
         }
 
@@ -1005,31 +1336,31 @@
                 ZenPolicy.STATE_UNSET);
 
         if (fullScreenIntent != ZenPolicy.STATE_UNSET) {
-            builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW);
+            builder.showFullScreenIntent(fullScreenIntent == STATE_ALLOW);
             policySet = true;
         }
         if (lights != ZenPolicy.STATE_UNSET) {
-            builder.showLights(lights == ZenPolicy.STATE_ALLOW);
+            builder.showLights(lights == STATE_ALLOW);
             policySet = true;
         }
         if (peek != ZenPolicy.STATE_UNSET) {
-            builder.showPeeking(peek == ZenPolicy.STATE_ALLOW);
+            builder.showPeeking(peek == STATE_ALLOW);
             policySet = true;
         }
         if (statusBar != ZenPolicy.STATE_UNSET) {
-            builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW);
+            builder.showStatusBarIcons(statusBar == STATE_ALLOW);
             policySet = true;
         }
         if (badges != ZenPolicy.STATE_UNSET) {
-            builder.showBadges(badges == ZenPolicy.STATE_ALLOW);
+            builder.showBadges(badges == STATE_ALLOW);
             policySet = true;
         }
         if (ambient != ZenPolicy.STATE_UNSET) {
-            builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW);
+            builder.showInAmbientDisplay(ambient == STATE_ALLOW);
             policySet = true;
         }
         if (notificationList != ZenPolicy.STATE_UNSET) {
-            builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW);
+            builder.showInNotificationList(notificationList == STATE_ALLOW);
             policySet = true;
         }
 
@@ -1266,17 +1597,22 @@
         }
     };
 
+    public ZenPolicy getZenPolicy() {
+        return Flags.modesUi() ? manualRule.zenPolicy : toZenPolicy();
+    }
+
     /**
      * Converts a ZenModeConfig to a ZenPolicy
      */
-    public ZenPolicy toZenPolicy() {
+    @VisibleForTesting
+    ZenPolicy toZenPolicy() {
         ZenPolicy.Builder builder = new ZenPolicy.Builder()
                 .allowCalls(allowCalls
-                        ? notificationPolicySendersToZenPolicyPeopleType(allowCallsFrom)
+                        ? prioritySendersToPeopleType(allowCallsFrom)
                         : ZenPolicy.PEOPLE_TYPE_NONE)
                 .allowRepeatCallers(allowRepeatCallers)
                 .allowMessages(allowMessages
-                        ? notificationPolicySendersToZenPolicyPeopleType(allowMessagesFrom)
+                        ? prioritySendersToPeopleType(allowMessagesFrom)
                         : ZenPolicy.PEOPLE_TYPE_NONE)
                 .allowReminders(allowReminders)
                 .allowEvents(allowEvents)
@@ -1336,7 +1672,7 @@
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES,
                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) {
             priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
-            messageSenders = zenPolicyPeopleTypeToNotificationPolicySenders(
+            messageSenders = peopleTypeToPrioritySenders(
                     zenPolicy.getPriorityMessageSenders(), messageSenders);
         }
 
@@ -1352,7 +1688,7 @@
         if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
                 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
             priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
-            callSenders = zenPolicyPeopleTypeToNotificationPolicySenders(
+            callSenders = peopleTypeToPrioritySenders(
                     zenPolicy.getPriorityCallSenders(), callSenders);
         }
 
@@ -1452,46 +1788,155 @@
         return (policy.suppressedVisualEffects & visualEffect) == 0;
     }
 
+    private boolean isVisualEffectAllowed(int suppressedVisualEffects, int visualEffect) {
+        return (suppressedVisualEffects & visualEffect) == 0;
+    }
+
     public Policy toNotificationPolicy() {
         int priorityCategories = 0;
         int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
         int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
         int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT;
-        if (allowConversations) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
-        }
-        if (allowCalls) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
-        }
-        if (allowMessages) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
-        }
-        if (allowEvents) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
-        }
-        if (allowReminders) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
-        }
-        if (allowRepeatCallers) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
-        }
-        if (allowAlarms) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
-        }
-        if (allowMedia) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
-        }
-        if (allowSystem) {
-            priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
-        }
-        priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
-        priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
-        priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
-                allowConversationsFrom, priorityConversationSenders);
+        int state = 0;
+        int suppressedVisualEffects = 0;
 
-        int state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0;
-        if (Flags.modesApi()) {
-            state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels);
+        if (Flags.modesUi()) {
+            if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, false)) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
+            }
+            if (manualRule.zenPolicy.isCategoryAllowed(
+                    ZenPolicy.PRIORITY_CATEGORY_REMINDERS, false)) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
+            }
+            if (manualRule.zenPolicy.isCategoryAllowed(
+                    ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, false)) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+            }
+            if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, false)) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
+            }
+            if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, false)) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
+            }
+            if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, false)) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
+            }
+
+            if (manualRule.zenPolicy.getPriorityCategoryConversations() == STATE_ALLOW) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+            }
+            priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
+                    manualRule.zenPolicy.getPriorityConversationSenders(),
+                    CONVERSATION_SENDERS_NONE);
+            if (manualRule.zenPolicy.getPriorityCategoryCalls() == STATE_ALLOW) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
+            }
+            priorityCallSenders = peopleTypeToPrioritySenders(
+                    manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE);
+            if (manualRule.zenPolicy.getPriorityCategoryMessages() == STATE_ALLOW) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
+            }
+            priorityMessageSenders = peopleTypeToPrioritySenders(
+                    manualRule.zenPolicy.getPriorityMessageSenders(), DEFAULT_SOURCE);
+
+            state = Policy.policyState(areChannelsBypassingDnd,
+                    manualRule.zenPolicy.getPriorityChannelsAllowed() != STATE_DISALLOW);
+
+            boolean suppressFullScreenIntent = !manualRule.zenPolicy.isVisualEffectAllowed(
+                    ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
+                    isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+                            ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT));
+
+            boolean suppressLights = !manualRule.zenPolicy.isVisualEffectAllowed(
+                    ZenPolicy.VISUAL_EFFECT_LIGHTS,
+                    isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+                            ZenPolicy.VISUAL_EFFECT_LIGHTS));
+
+            boolean suppressAmbient = !manualRule.zenPolicy.isVisualEffectAllowed(
+                    ZenPolicy.VISUAL_EFFECT_AMBIENT,
+                    isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+                            ZenPolicy.VISUAL_EFFECT_AMBIENT));
+
+            if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
+                suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
+            }
+
+            if (suppressFullScreenIntent) {
+                suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+            }
+
+            if (suppressLights) {
+                suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
+            }
+
+            if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK,
+                    isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+                            ZenPolicy.VISUAL_EFFECT_PEEK))) {
+                suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
+                suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
+            }
+
+            if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
+                    isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+                            ZenPolicy.VISUAL_EFFECT_STATUS_BAR))) {
+                suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+            }
+
+            if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE,
+                    isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+                            ZenPolicy.VISUAL_EFFECT_BADGE))) {
+                suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
+            }
+
+            if (suppressAmbient) {
+                suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT;
+            }
+
+            if (!manualRule.zenPolicy.isVisualEffectAllowed(
+                    ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
+                    isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS,
+                            ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST))) {
+                suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+            }
+        } else {
+            if (isAllowConversations()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS;
+            }
+            if (isAllowCalls()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
+            }
+            if (isAllowMessages()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
+            }
+            if (isAllowEvents()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
+            }
+            if (isAllowReminders()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
+            }
+            if (isAllowRepeatCallers()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
+            }
+            if (isAllowAlarms()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
+            }
+            if (isAllowMedia()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
+            }
+            if (isAllowSystem()) {
+                priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
+            }
+            priorityCallSenders = sourceToPrioritySenders(getAllowCallsFrom(), priorityCallSenders);
+            priorityMessageSenders = sourceToPrioritySenders(
+                    getAllowMessagesFrom(), priorityMessageSenders);
+            priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy(
+                    getAllowConversationsFrom(), priorityConversationSenders);
+
+            state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0;
+            if (Flags.modesApi()) {
+                state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels);
+            }
+            suppressedVisualEffects = getSuppressedVisualEffects();
         }
 
         return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
@@ -1544,31 +1989,38 @@
 
     public void applyNotificationPolicy(Policy policy) {
         if (policy == null) return;
-        allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
-        allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
-        allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
-        allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
-        allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
-        allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
-        allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
-        allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
-                != 0;
-        allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom);
-        allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders,
-                allowMessagesFrom);
-        if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
-            suppressedVisualEffects = policy.suppressedVisualEffects;
+        if (Flags.modesUi()) {
+            manualRule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy);
+        } else {
+            setAllowAlarms((policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0);
+            allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
+            allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
+            allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
+            allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
+            allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
+            allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
+            allowRepeatCallers =
+                    (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
+                            != 0;
+            allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom);
+            allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders,
+                    allowMessagesFrom);
+            if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
+                suppressedVisualEffects = policy.suppressedVisualEffects;
+            }
+            allowConversations = (policy.priorityCategories
+                    & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
+            allowConversationsFrom = normalizeConversationSenders(allowConversations,
+                    policy.priorityConversationSenders,
+                    allowConversationsFrom);
+            if (policy.state != Policy.STATE_UNSET) {
+                if (Flags.modesApi()) {
+                    setAllowPriorityChannels(policy.allowPriorityChannels());
+                }
+            }
         }
-        allowConversations = (policy.priorityCategories
-                & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0;
-        allowConversationsFrom = normalizeConversationSenders(allowConversations,
-                policy.priorityConversationSenders,
-                allowConversationsFrom);
         if (policy.state != Policy.STATE_UNSET) {
             areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
-            if (Flags.modesApi()) {
-                allowPriorityChannels = policy.allowPriorityChannels();
-            }
         }
     }
 
@@ -1995,49 +2447,11 @@
         return "";
     }
 
-    public static String getConditionSummary(Context context, ZenModeConfig config,
-            int userHandle, boolean shortVersion) {
-        return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
-    }
-
-    private static String getConditionLine(Context context, ZenModeConfig config,
-            int userHandle, boolean useLine1, boolean shortVersion) {
-        if (config == null) return "";
-        String summary = "";
-        if (config.manualRule != null) {
-            final Uri id = config.manualRule.conditionId;
-            if (config.manualRule.enabler != null) {
-                summary = getOwnerCaption(context, config.manualRule.enabler);
-            } else {
-                if (id == null) {
-                    summary = context.getString(com.android.internal.R.string.zen_mode_forever);
-                } else {
-                    final long time = tryParseCountdownConditionId(id);
-                    Condition c = config.manualRule.condition;
-                    if (time > 0) {
-                        final long now = System.currentTimeMillis();
-                        final long span = time - now;
-                        c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
-                                userHandle, shortVersion);
-                    }
-                    final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
-                    summary = TextUtils.isEmpty(rt) ? "" : rt;
-                }
-            }
+    public boolean isManualActive() {
+        if (!Flags.modesUi()) {
+            return manualRule != null;
         }
-        for (ZenRule automaticRule : config.automaticRules.values()) {
-            if (automaticRule.isAutomaticActive()) {
-                if (summary.isEmpty()) {
-                    summary = automaticRule.name;
-                } else {
-                    summary = context.getResources()
-                            .getString(R.string.zen_mode_rule_name_combination, summary,
-                                    automaticRule.name);
-                }
-
-            }
-        }
-        return summary;
+        return manualRule != null && manualRule.isAutomaticActive();
     }
 
     public static class ZenRule implements Parcelable {
@@ -2401,7 +2815,7 @@
     public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) {
         return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
                 || zen == Global.ZEN_MODE_ALARMS
-                || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                || (zen == ZEN_MODE_IMPORTANT_INTERRUPTIONS
                 && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy));
     }
 
@@ -2410,21 +2824,40 @@
      * This includes notification, ringer and system sounds
      */
     public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) {
-        boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd;
-        if (Flags.modesApi()) {
-            areChannelsBypassingDnd = config.areChannelsBypassingDnd
-                    && config.allowPriorityChannels;
+        if (Flags.modesUi()) {
+            final ZenPolicy policy = config.manualRule.zenPolicy;
+            return !policy.isCategoryAllowed(PRIORITY_CATEGORY_REMINDERS, false)
+                    && !policy.isCategoryAllowed(PRIORITY_CATEGORY_CALLS, false)
+                    && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MESSAGES, false)
+                    && !policy.isCategoryAllowed(PRIORITY_CATEGORY_EVENTS, false)
+                    && !policy.isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false)
+                    && !policy.isCategoryAllowed(PRIORITY_CATEGORY_SYSTEM, false)
+                    && !(config.areChannelsBypassingDnd && policy.getPriorityChannelsAllowed()
+                    == STATE_ALLOW);
+
+        } else {
+            boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd;
+            if (Flags.modesApi()) {
+                areChannelsBypassingDnd = config.areChannelsBypassingDnd
+                        && config.isAllowPriorityChannels();
+            }
+            return !config.isAllowReminders() && !config.isAllowCalls() && !config.isAllowMessages()
+                    && !config.isAllowEvents() && !config.isAllowRepeatCallers()
+                    && !areChannelsBypassingDnd && !config.isAllowSystem();
         }
-        return !config.allowReminders && !config.allowCalls && !config.allowMessages
-                && !config.allowEvents && !config.allowRepeatCallers
-                && !areChannelsBypassingDnd && !config.allowSystem;
     }
 
     /**
      * Determines whether dnd mutes all sounds
      */
     public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
-        return !config.allowAlarms  && !config.allowMedia
+        if (Flags.modesUi()) {
+            final ZenPolicy policy = config.manualRule.zenPolicy;
+            return !policy.isCategoryAllowed(PRIORITY_CATEGORY_ALARMS, false)
+                    && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MEDIA, false)
+                    && areAllPriorityOnlyRingerSoundsMuted(config);
+        }
+        return !config.isAllowAlarms()  && !config.isAllowMedia()
                 && areAllPriorityOnlyRingerSoundsMuted(config);
     }
 
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
index 2aa17c4..1af3b0f 100644
--- a/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceSandboxedInferenceService.aidl
@@ -21,6 +21,7 @@
 import android.app.ondeviceintelligence.ITokenInfoCallback;
 import android.app.ondeviceintelligence.IProcessingSignal;
 import android.app.ondeviceintelligence.Feature;
+import android.os.IRemoteCallback;
 import android.os.ICancellationSignal;
 import android.os.PersistableBundle;
 import android.os.Bundle;
@@ -34,18 +35,19 @@
  * @hide
  */
 oneway interface IOnDeviceSandboxedInferenceService {
-    void registerRemoteStorageService(in IRemoteStorageService storageService);
+    void registerRemoteStorageService(in IRemoteStorageService storageService,
+                                        in IRemoteCallback remoteCallback) = 0;
     void requestTokenInfo(int callerUid, in Feature feature, in Bundle request,
                             in AndroidFuture cancellationSignal,
-                            in ITokenInfoCallback tokenInfoCallback);
+                            in ITokenInfoCallback tokenInfoCallback) = 1;
     void processRequest(int callerUid, in Feature feature, in Bundle request, in int requestType,
                         in AndroidFuture cancellationSignal,
                         in AndroidFuture processingSignal,
-                        in IResponseCallback callback);
+                        in IResponseCallback callback) = 2;
     void processRequestStreaming(int callerUid, in Feature feature, in Bundle request, in int requestType,
                                 in AndroidFuture cancellationSignal,
                                 in AndroidFuture processingSignal,
-                                in IStreamingResponseCallback callback);
+                                in IStreamingResponseCallback callback) = 3;
     void updateProcessingState(in Bundle processingState,
-                                     in IProcessingUpdateStatusCallback callback);
+                                     in IProcessingUpdateStatusCallback callback) = 4;
 }
\ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
index 293015f..d82fe1c 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -59,6 +59,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -224,6 +225,7 @@
                                         Bundle bundle = new Bundle();
                                         parcelFileDescriptorMap.forEach(bundle::putParcelable);
                                         remoteCallback.sendResult(bundle);
+                                        tryClosePfds(parcelFileDescriptorMap.values());
                                     }));
                 }
 
@@ -444,6 +446,16 @@
         };
     }
 
+    private static void tryClosePfds(Collection<ParcelFileDescriptor> pfds) {
+        pfds.forEach(pfd -> {
+            try {
+                pfd.close();
+            } catch (Exception e) {
+                Log.w(TAG, "Error closing FD", e);
+            }
+        });
+    }
+
     private void onGetReadOnlyFileDescriptor(@NonNull String fileName,
             @NonNull AndroidFuture<ParcelFileDescriptor> future) {
         Slog.v(TAG, "onGetReadOnlyFileDescriptor " + fileName);
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
index d00485c..f123a96 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -51,6 +51,7 @@
 import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.OutcomeReceiver;
 import android.os.ParcelFileDescriptor;
@@ -100,6 +101,11 @@
     private static final String TAG = OnDeviceSandboxedInferenceService.class.getSimpleName();
 
     /**
+     * @hide
+     */
+    public static final String INFERENCE_INFO_BUNDLE_KEY = "inference_info";
+
+    /**
      * The {@link Intent} that must be declared as handled by the service. To be supported, the
      * service must also require the
      * {@link android.Manifest.permission#BIND_ON_DEVICE_SANDBOXED_INFERENCE_SERVICE}
@@ -148,9 +154,12 @@
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
             return new IOnDeviceSandboxedInferenceService.Stub() {
                 @Override
-                public void registerRemoteStorageService(IRemoteStorageService storageService) {
+                public void registerRemoteStorageService(IRemoteStorageService storageService,
+                        IRemoteCallback remoteCallback) throws RemoteException {
                     Objects.requireNonNull(storageService);
                     mRemoteStorageService = storageService;
+                    remoteCallback.sendResult(
+                            Bundle.EMPTY); //to notify caller uid to system-server.
                 }
 
                 @Override
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 9589785..8271caf 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -29,6 +29,7 @@
 
 import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION;
 import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
+import static com.android.window.flags.Flags.noVisibilityEventOnDisplayStateChange;
 import static com.android.window.flags.Flags.offloadColorExtraction;
 import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
 
@@ -2387,8 +2388,10 @@
                     @Override
                     public void onDisplayChanged(int displayId) {
                         if (mDisplay.getDisplayId() == displayId) {
-                            boolean forceReport = mIsWearOs
-                                    && mDisplay.getState() != Display.STATE_DOZE_SUSPEND;
+                            boolean forceReport =
+                                    !noVisibilityEventOnDisplayStateChange()
+                                            && mIsWearOs
+                                            && mDisplay.getState() != Display.STATE_DOZE_SUSPEND;
                             reportVisibility(forceReport);
                         }
                     }
diff --git a/core/java/android/service/wearable/WearableSensingService.java b/core/java/android/service/wearable/WearableSensingService.java
index ac22e70..3735c43 100644
--- a/core/java/android/service/wearable/WearableSensingService.java
+++ b/core/java/android/service/wearable/WearableSensingService.java
@@ -398,8 +398,8 @@
     /**
      * Called when a data request observer is registered. Each request must not be larger than
      * {@link WearableSensingDataRequest#getMaxRequestSize()}. In addition, at most {@link
-     * WearableSensingDataRequester#getRateLimit()} requests can be sent every rolling {@link
-     * WearableSensingDataRequester#getRateLimitWindowSize()}. Requests that are too large or too
+     * WearableSensingDataRequest#getRateLimit()} requests can be sent every rolling {@link
+     * WearableSensingDataRequest#getRateLimitWindowSize()}. Requests that are too large or too
      * frequent will be dropped by the system. See {@link
      * WearableSensingDataRequester#requestData(WearableSensingDataRequest, Consumer)} for details
      * about the status code returned for each request.
@@ -442,7 +442,7 @@
      * @param packageName The package name of the app that will receive the requests sent to the
      *     dataRequester.
      * @param dataRequester A handle to the observer to be unregistered. It is the exact same
-     *     instance provided in a previous {@link #onDataRequestConsumerRegistered(int, String,
+     *     instance provided in a previous {@link #onDataRequestObserverRegistered(int, String,
      *     WearableSensingDataRequester, Consumer)} invocation.
      * @param statusConsumer the consumer for the status of the data request observer
      *     unregistration. This is different from the status for each data request.
@@ -469,7 +469,7 @@
      * in which case it should return the corresponding status code.
      *
      * <p>The implementation should also store the {@code statusConsumer}. If the wearable stops
-     * listening for hotword for any reason other than {@link #onStopListeningForHotword(Consumer)}
+     * listening for hotword for any reason other than {@link #onStopHotwordRecognition(Consumer)}
      * being invoked, it should send an appropriate status code listed in {@link
      * WearableSensingManager} to {@code statusConsumer}. If the error condition cannot be described
      * by any of those status codes, it should send a {@link WearableSensingManager#STATUS_UNKNOWN}.
@@ -514,11 +514,11 @@
 
     /**
      * Called when hotword audio data sent to the {@code hotwordAudioConsumer} in {@link
-     * #onStartListeningForHotword(Consumer, Consumer)} is accepted by the
+     * #onStartHotwordRecognition(Consumer, Consumer)} is accepted by the
      * {@link android.service.voice.HotwordDetectionService} as valid hotword.
      *
      * <p>After the implementation of this class sends the hotword audio data to the {@code
-     * hotwordAudioConsumer} in {@link #onStartListeningForHotword(Consumer,
+     * hotwordAudioConsumer} in {@link #onStartHotwordRecognition(Consumer,
      * Consumer)}, the system will forward the data into {@link
      * android.service.voice.HotwordDetectionService} (which runs in an isolated process) for
      * second-stage hotword detection. If accepted as valid hotword there, this method will be
@@ -545,7 +545,7 @@
      *
      * <p>This method is expected to be overridden by a derived class. The implementation should
      * stop sending hotword audio data to the {@code hotwordAudioConsumer} in {@link
-     * #onStartListeningForHotword(Consumer, Consumer)}
+     * #onStartHotwordRecognition(Consumer, Consumer)}
      */
     @FlaggedApi(Flags.FLAG_ENABLE_HOTWORD_WEARABLE_SENSING_API)
     @BinderThread
diff --git a/core/java/android/telephony/DropBoxManagerLoggerBackend.java b/core/java/android/telephony/DropBoxManagerLoggerBackend.java
new file mode 100644
index 0000000..25a3b9f
--- /dev/null
+++ b/core/java/android/telephony/DropBoxManagerLoggerBackend.java
@@ -0,0 +1,261 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.DropBoxManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.Optional;
+
+/**
+ * A persistent logger backend that stores logs in Android DropBoxManager
+ *
+ * @hide
+ */
+public class DropBoxManagerLoggerBackend implements PersistentLoggerBackend {
+
+    private static final String TAG = "DropBoxManagerLoggerBackend";
+    // Separate tag reference to be explicitly used for dropboxmanager instead of logcat logging
+    private static final String DROPBOX_TAG = "DropBoxManagerLoggerBackend";
+    private static final DateTimeFormatter LOG_TIMESTAMP_FORMATTER =
+            DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS");
+    private static final ZoneId LOCAL_ZONE_ID = ZoneId.systemDefault();
+    private static final int BUFFER_SIZE_BYTES = 500 * 1024; // 500 KB
+    private static final int MIN_BUFFER_BYTES_FOR_FLUSH = 5 * 1024; // 5 KB
+
+    private static DropBoxManagerLoggerBackend sInstance;
+
+    private final DropBoxManager mDropBoxManager;
+    private final Object mBufferLock = new Object();
+    @GuardedBy("mBufferLock")
+    private final StringBuilder mLogBuffer = new StringBuilder();
+    private long mBufferStartTime = -1L;
+    private final HandlerThread mHandlerThread = new HandlerThread(DROPBOX_TAG);
+    private final Handler mHandler;
+    // Flag for determining if logging is enabled as a general feature
+    private final boolean mDropBoxManagerLoggingEnabled;
+    // Flag for controlling if logging is enabled at runtime
+    private boolean mIsLoggingEnabled = false;
+
+    /**
+     * Returns a singleton instance of {@code DropBoxManagerLoggerBackend} that will log to
+     * DropBoxManager if the config_dropboxmanager_persistent_logging_enabled resource config is
+     * enabled.
+     * @param context Android context
+     */
+    @Nullable
+    public static synchronized DropBoxManagerLoggerBackend getInstance(@NonNull Context context) {
+        if (sInstance == null) {
+            sInstance = new DropBoxManagerLoggerBackend(context);
+        }
+        return sInstance;
+    }
+
+    private DropBoxManagerLoggerBackend(@NonNull Context context) {
+        mDropBoxManager = context.getSystemService(DropBoxManager.class);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mDropBoxManagerLoggingEnabled = persistentLoggingEnabled(context);
+    }
+
+    private boolean persistentLoggingEnabled(@NonNull Context context) {
+        try {
+            return context.getResources().getBoolean(
+                    R.bool.config_dropboxmanager_persistent_logging_enabled);
+        } catch (RuntimeException e) {
+            Log.w(TAG, "Persistent logging config not found");
+            return false;
+        }
+    }
+
+    /**
+     * Enable or disable logging to DropBoxManager
+     * @param isLoggingEnabled Whether logging should be enabled
+     */
+    public void setLoggingEnabled(boolean isLoggingEnabled) {
+        Log.i(DROPBOX_TAG, "toggle logging: " + isLoggingEnabled);
+        mIsLoggingEnabled = isLoggingEnabled;
+    }
+
+    /**
+     * Persist a DEBUG log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public void debug(@NonNull String tag, @NonNull String msg) {
+        if (!mDropBoxManagerLoggingEnabled) {
+            return;
+        }
+        bufferLog("D", tag, msg, Optional.empty());
+    }
+
+    /**
+     * Persist a INFO log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public void info(@NonNull String tag, @NonNull String msg) {
+        if (!mDropBoxManagerLoggingEnabled) {
+            return;
+        }
+        bufferLog("I", tag, msg, Optional.empty());
+    }
+
+    /**
+     * Persist a WARN log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public void warn(@NonNull String tag, @NonNull String msg) {
+        if (!mDropBoxManagerLoggingEnabled) {
+            return;
+        }
+        bufferLog("W", tag, msg, Optional.empty());
+    }
+
+    /**
+     * Persist a WARN log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @param t An exception to log.
+     */
+    public void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) {
+        if (!mDropBoxManagerLoggingEnabled) {
+            return;
+        }
+        bufferLog("W", tag, msg, Optional.of(t));
+    }
+
+    /**
+     * Persist a ERROR log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public void error(@NonNull String tag, @NonNull String msg) {
+        if (!mDropBoxManagerLoggingEnabled) {
+            return;
+        }
+        bufferLog("E", tag, msg, Optional.empty());
+    }
+
+    /**
+     * Persist a ERROR log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @param t An exception to log.
+     */
+    public void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) {
+        if (!mDropBoxManagerLoggingEnabled) {
+            return;
+        }
+        bufferLog("E", tag, msg, Optional.of(t));
+    }
+
+    private synchronized void bufferLog(
+            @NonNull String level,
+            @NonNull String tag,
+            @NonNull String msg,
+            Optional<Throwable> t) {
+        if (!mIsLoggingEnabled) {
+            return;
+        }
+
+        if (mBufferStartTime == -1L) {
+            mBufferStartTime = System.currentTimeMillis();
+        }
+
+        synchronized (mBufferLock) {
+            mLogBuffer
+                    .append(formatLog(level, tag, msg, t))
+                    .append("\n");
+
+            if (mLogBuffer.length() >= BUFFER_SIZE_BYTES) {
+                flushAsync();
+            }
+        }
+    }
+
+    private String formatLog(
+            @NonNull String level,
+            @NonNull String tag,
+            @NonNull String msg,
+            Optional<Throwable> t) {
+        // Expected format = "$Timestamp $Level $Tag: $Message"
+        return formatTimestamp(System.currentTimeMillis()) + " " + level + " " + tag + ": "
+                + t.map(throwable -> msg + ": " + Log.getStackTraceString(throwable)).orElse(msg);
+    }
+
+    private String formatTimestamp(long currentTimeMillis) {
+        return Instant.ofEpochMilli(currentTimeMillis)
+                .atZone(LOCAL_ZONE_ID)
+                .format(LOG_TIMESTAMP_FORMATTER);
+    }
+
+    /**
+     * Flushes all buffered logs into DropBoxManager as a single log record with a tag of
+     * {@link #DROPBOX_TAG} asynchronously. Should be invoked sparingly as DropBoxManager has
+     * device-level limitations on the number files that can be stored.
+     */
+    public void flushAsync() {
+        if (!mDropBoxManagerLoggingEnabled) {
+            return;
+        }
+
+        mHandler.post(this::flush);
+    };
+
+    /**
+     * Flushes all buffered logs into DropBoxManager as a single log record with a tag of
+     * {@link #DROPBOX_TAG}. Should be invoked sparingly as DropBoxManager has device-level
+     * limitations on the number files that can be stored.
+     */
+    public void flush() {
+        if (!mDropBoxManagerLoggingEnabled) {
+            return;
+        }
+
+        synchronized (mBufferLock) {
+            if (mLogBuffer.length() < MIN_BUFFER_BYTES_FOR_FLUSH) {
+                return;
+            }
+
+            Log.d(DROPBOX_TAG, "Flushing logs from "
+                    + formatTimestamp(mBufferStartTime) + " to "
+                    + formatTimestamp(System.currentTimeMillis()));
+
+            try {
+                mDropBoxManager.addText(DROPBOX_TAG, mLogBuffer.toString());
+            } catch (Exception e) {
+                Log.w(DROPBOX_TAG, "Failed to flush logs of length "
+                        + mLogBuffer.length() + " to DropBoxManager", e);
+            }
+            mLogBuffer.setLength(0);
+        }
+        mBufferStartTime = -1L;
+    }
+}
diff --git a/core/java/android/telephony/PersistentLogger.java b/core/java/android/telephony/PersistentLogger.java
new file mode 100644
index 0000000..8b12a1c
--- /dev/null
+++ b/core/java/android/telephony/PersistentLogger.java
@@ -0,0 +1,89 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+
+/**
+ * A persistent logging client. Intended for persisting critical debug logs in situations where
+ * standard Android logcat logs may not be retained long enough.
+ *
+ * @hide
+ */
+public class PersistentLogger {
+    private final PersistentLoggerBackend mPersistentLoggerBackend;
+
+    public PersistentLogger(@NonNull PersistentLoggerBackend persistentLoggerBackend) {
+        mPersistentLoggerBackend = persistentLoggerBackend;
+    }
+
+    /**
+     * Persist a DEBUG log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public void debug(@NonNull String tag, @NonNull String msg) {
+        mPersistentLoggerBackend.debug(tag, msg);
+    }
+
+    /**
+     * Persist a INFO log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public void info(@NonNull String tag, @NonNull String msg) {
+        mPersistentLoggerBackend.info(tag, msg);
+    }
+
+    /**
+     * Persist a WARN log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public void warn(@NonNull String tag, @NonNull String msg) {
+        mPersistentLoggerBackend.warn(tag, msg);
+    }
+
+    /**
+     * Persist a WARN log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @param t An exception to log.
+     */
+    public void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) {
+        mPersistentLoggerBackend.warn(tag, msg, t);
+    }
+
+    /**
+     * Persist a ERROR log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    public void error(@NonNull String tag, @NonNull String msg) {
+        mPersistentLoggerBackend.error(tag, msg);
+    }
+
+    /**
+     * Persist a ERROR log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @param t An exception to log.
+     */
+    public void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t) {
+        mPersistentLoggerBackend.error(tag, msg, t);
+    }
+}
diff --git a/core/java/android/telephony/PersistentLoggerBackend.java b/core/java/android/telephony/PersistentLoggerBackend.java
new file mode 100644
index 0000000..e3e72e1
--- /dev/null
+++ b/core/java/android/telephony/PersistentLoggerBackend.java
@@ -0,0 +1,71 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface for logging backends to provide persistent log storage.
+ *
+ * @hide
+ */
+public interface PersistentLoggerBackend {
+
+    /**
+     * Persist a DEBUG log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    void debug(@NonNull String tag, @NonNull String msg);
+
+    /**
+     * Persist a INFO log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    void info(@NonNull String tag, @NonNull String msg);
+
+    /**
+     * Persist a WARN log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    void warn(@NonNull String tag, @NonNull String msg);
+
+    /**
+     * Persist a WARN log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @param t An exception to log.
+     */
+    void warn(@NonNull String tag, @NonNull String msg, @NonNull Throwable t);
+
+    /**
+     * Persist a ERROR log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     */
+    void error(@NonNull String tag, @NonNull String msg);
+
+    /**
+     * Persist a ERROR log message.
+     * @param tag Used to identify the source of a log message.
+     * @param msg The message you would like logged.
+     * @param t An exception to log.
+     */
+    void error(@NonNull String tag, @NonNull String msg, @NonNull Throwable t);
+}
diff --git a/core/java/android/telephony/SubscriptionPlan.aidl b/core/java/android/telephony/SubscriptionPlan.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index 70dc300e..8836c8a 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -191,3 +191,32 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "disable_handwriting_initiator_for_ime"
+  namespace: "text"
+  description: "Don't initiate handwriting for IME views."
+  bug: "343304685"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "fix_null_typeface_bolding"
+  namespace: "text"
+  description: "Use a bold typeface when bolding is enabled and the original typeface is null"
+  bug: "314811487"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "rust_hyphenator"
+  namespace: "text"
+  description: "Reimplement hyphenator for safe file read"
+  # Hyphenator is initialized in Zygote
+  is_fixed_read_only: true
+  bug: "346915432"
+}
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
old mode 100755
new mode 100644
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
old mode 100755
new mode 100644
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 815fc58..0714285 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -220,7 +220,7 @@
         DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE, "false");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_PHASE2, "false");
-        DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_METRICS, "false");
+        DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_METRICS, "true");
         DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
         DEFAULT_FLAGS.put(SETTINGS_SHOW_STYLUS_PREFERENCES, "true");
         DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false");
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 57d1b8d..ccec89b 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -496,9 +496,10 @@
         if (delegatorPackageName == null) {
             delegatorPackageName = view.getContext().getOpPackageName();
         }
+        WeakReference<View> viewRef = new WeakReference<>(view);
         Consumer<Boolean> consumer = delegationAccepted -> {
             if (delegationAccepted) {
-                onDelegationAccepted(view);
+                onDelegationAccepted(viewRef.get());
             }
         };
         mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName, view::post, consumer);
@@ -509,6 +510,10 @@
             mState.mHandled = true;
             mState.mShouldInitHandwriting = false;
         }
+        if (view == null) {
+            // can be null if view was detached and was GCed.
+            return;
+        }
         if (view instanceof TextView) {
             ((TextView) view).hideHint();
         }
diff --git a/core/java/android/view/HdrRenderState.java b/core/java/android/view/HdrRenderState.java
index eadc507..c6b3937 100644
--- a/core/java/android/view/HdrRenderState.java
+++ b/core/java/android/view/HdrRenderState.java
@@ -65,6 +65,7 @@
     void startListening() {
         if (isHdrEnabled() && !mIsListenerRegistered && mViewRoot.mDisplay != null) {
             mViewRoot.mDisplay.registerHdrSdrRatioChangedListener(mViewRoot.mExecutor, this);
+            mIsListenerRegistered = true;
         }
     }
 
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 8d884f2..02ea6d4 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -817,7 +817,7 @@
                     Collections.unmodifiableList(runningAnimations));
             if (DEBUG) {
                 for (WindowInsetsAnimation anim : runningAnimations) {
-                    Log.d(TAG, String.format("Running animation type: %d, progress: %f",
+                    Log.d(TAG, String.format("Running animation on insets type: %d, progress: %f",
                             anim.getTypeMask(), anim.getInterpolatedFraction()));
                 }
             }
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 71199e9..c302126 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -43,8 +44,13 @@
 import android.util.SparseArray;
 import android.view.flags.Flags;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.util.XmlUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Represents an icon that can be used as a mouse pointer.
  * <p>
@@ -164,6 +170,32 @@
     // every time we need to resolve the icon (i.e. on each input event).
     private static final SparseArray<PointerIcon> SYSTEM_ICONS = new SparseArray<>();
 
+    /** @hide */
+    @IntDef(prefix = {"POINTER_ICON_VECTOR_STYLE_FILL_"}, value = {
+            POINTER_ICON_VECTOR_STYLE_FILL_BLACK,
+            POINTER_ICON_VECTOR_STYLE_FILL_GREEN,
+            POINTER_ICON_VECTOR_STYLE_FILL_YELLOW,
+            POINTER_ICON_VECTOR_STYLE_FILL_PINK,
+            POINTER_ICON_VECTOR_STYLE_FILL_BLUE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PointerIconVectorStyleFill {}
+
+    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_BLACK = 0;
+    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_GREEN = 1;
+    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_YELLOW = 2;
+    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_PINK = 3;
+    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_BLUE = 4;
+
+    // If adding a PointerIconVectorStyleFill, update END value for {@link SystemSettingsValidators}
+    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_BEGIN =
+            POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
+    /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_END =
+            POINTER_ICON_VECTOR_STYLE_FILL_BLUE;
+
+    /** @hide */ public static final float DEFAULT_POINTER_SCALE = 1f;
+    /** @hide */ public static final float LARGE_POINTER_SCALE = 2.5f;
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final int mType;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -224,7 +256,7 @@
      * @hide
      */
     public static @NonNull PointerIcon getLoadedSystemIcon(@NonNull Context context, int type,
-            boolean useLargeIcons) {
+            boolean useLargeIcons, float pointerScale) {
         if (type == TYPE_NOT_SPECIFIED) {
             throw new IllegalStateException("Cannot load icon for type TYPE_NOT_SPECIFIED");
         }
@@ -239,13 +271,18 @@
         }
 
         final int defStyle;
-        // TODO(b/305193969): Use scaled vectors when large icons are requested.
-        if (useLargeIcons) {
-            defStyle = com.android.internal.R.style.LargePointer;
-        } else if (android.view.flags.Flags.enableVectorCursors()) {
+        if (android.view.flags.Flags.enableVectorCursorA11ySettings()) {
             defStyle = com.android.internal.R.style.VectorPointer;
         } else {
-            defStyle = com.android.internal.R.style.Pointer;
+            // TODO(b/346358375): Remove useLargeIcons and the legacy pointer styles when
+            //  enableVectorCursorA11ySetting is rolled out.
+            if (useLargeIcons) {
+                defStyle = com.android.internal.R.style.LargePointer;
+            } else if (android.view.flags.Flags.enableVectorCursors()) {
+                defStyle = com.android.internal.R.style.VectorPointer;
+            } else {
+                defStyle = com.android.internal.R.style.Pointer;
+            }
         }
         TypedArray a = context.obtainStyledAttributes(null,
                 com.android.internal.R.styleable.Pointer,
@@ -257,11 +294,11 @@
             Log.w(TAG, "Missing theme resources for pointer icon type " + type);
             return type == TYPE_DEFAULT
                     ? getSystemIcon(TYPE_NULL)
-                    : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons);
+                    : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons, pointerScale);
         }
 
         final PointerIcon icon = new PointerIcon(type);
-        icon.loadResource(context.getResources(), resourceId);
+        icon.loadResource(context.getResources(), resourceId, context.getTheme(), pointerScale);
         return icon;
     }
 
@@ -324,7 +361,7 @@
         }
 
         PointerIcon icon = new PointerIcon(TYPE_CUSTOM);
-        icon.loadResource(resources, resourceId);
+        icon.loadResource(resources, resourceId, null, DEFAULT_POINTER_SCALE);
         return icon;
     }
 
@@ -431,19 +468,21 @@
     }
 
     private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources,
-            VectorDrawable vectorDrawable) {
+            VectorDrawable vectorDrawable, float pointerScale) {
         // Ensure we pass the display metrics into the Bitmap constructor so that it is initialized
         // with the correct density.
         Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(),
-                vectorDrawable.getIntrinsicWidth(),
-                vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */);
+                (int) (vectorDrawable.getIntrinsicWidth() * pointerScale),
+                (int) (vectorDrawable.getIntrinsicHeight() * pointerScale),
+                Bitmap.Config.ARGB_8888, true /* hasAlpha */);
         Canvas canvas = new Canvas(bitmap);
         vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
         vectorDrawable.draw(canvas);
         return new BitmapDrawable(resources, bitmap);
     }
 
-    private void loadResource(@NonNull Resources resources, @XmlRes int resourceId) {
+    private void loadResource(@NonNull Resources resources, @XmlRes int resourceId,
+            @Nullable Resources.Theme theme, float pointerScale) {
         final XmlResourceParser parser = resources.getXml(resourceId);
         final int bitmapRes;
         final float hotSpotX;
@@ -454,8 +493,10 @@
             final TypedArray a = resources.obtainAttributes(
                     parser, com.android.internal.R.styleable.PointerIcon);
             bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
-            hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
-            hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
+            hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0)
+                    * pointerScale;
+            hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0)
+                    * pointerScale;
             a.recycle();
         } catch (Exception ex) {
             throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex);
@@ -467,7 +508,7 @@
             throw new IllegalArgumentException("<pointer-icon> is missing bitmap attribute.");
         }
 
-        Drawable drawable = resources.getDrawable(bitmapRes);
+        Drawable drawable = resources.getDrawable(bitmapRes, theme);
         if (drawable instanceof AnimationDrawable) {
             // Extract animation frame bitmaps.
             final AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
@@ -504,7 +545,7 @@
                     }
                     if (isVectorAnimation) {
                         drawableFrame = getBitmapDrawableFromVectorDrawable(resources,
-                                (VectorDrawable) drawableFrame);
+                                (VectorDrawable) drawableFrame, pointerScale);
                     }
                     mBitmapFrames[i - 1] = getBitmapFromDrawable((BitmapDrawable) drawableFrame);
                 }
@@ -512,7 +553,8 @@
         }
         if (drawable instanceof VectorDrawable) {
             mDrawNativeDropShadow = true;
-            drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable);
+            drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable,
+                    pointerScale);
         }
         if (!(drawable instanceof BitmapDrawable)) {
             throw new IllegalArgumentException("<pointer-icon> bitmap attribute must "
@@ -649,6 +691,27 @@
     }
 
     /**
+     * Convert fill style constant to resource ID.
+     *
+     * @hide
+     */
+    public static int vectorFillStyleToResource(@PointerIconVectorStyleFill int fillStyle) {
+        return switch (fillStyle) {
+            case POINTER_ICON_VECTOR_STYLE_FILL_BLACK ->
+                    com.android.internal.R.style.PointerIconVectorStyleFillBlack;
+            case POINTER_ICON_VECTOR_STYLE_FILL_GREEN ->
+                    com.android.internal.R.style.PointerIconVectorStyleFillGreen;
+            case POINTER_ICON_VECTOR_STYLE_FILL_YELLOW ->
+                    com.android.internal.R.style.PointerIconVectorStyleFillYellow;
+            case POINTER_ICON_VECTOR_STYLE_FILL_PINK ->
+                    com.android.internal.R.style.PointerIconVectorStyleFillPink;
+            case POINTER_ICON_VECTOR_STYLE_FILL_BLUE ->
+                    com.android.internal.R.style.PointerIconVectorStyleFillBlue;
+            default -> com.android.internal.R.style.PointerIconVectorStyleFillBlack;
+        };
+    }
+
+    /**
      * Sets whether drop shadow will draw in the native code.
      *
      * @hide
@@ -658,4 +721,14 @@
     public void setDrawNativeDropShadow(boolean drawNativeDropShadow) {
         mDrawNativeDropShadow = drawNativeDropShadow;
     }
+
+    /**
+     * Gets the PointerIcon's bitmap.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public Bitmap getBitmap() {
+        return mBitmap;
+    }
 }
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 127d4a7..aa3654d 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -342,12 +342,14 @@
             return false;
         }
         final boolean matchName = !sCallStackDebuggingMatchName.isEmpty();
-        if (matchName && (name == null
-                || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) {
-            // Skip if target surface doesn't match requested surface
+        if (!matchName) {
+            return true;
+        }
+        if (name == null) {
             return false;
         }
-        return true;
+        return sCallStackDebuggingMatchName.contains(name.toLowerCase()) ||
+                        name.toLowerCase().contains(sCallStackDebuggingMatchName);
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index de81828..9bc1511 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7152,7 +7152,7 @@
     public void setPendingCredentialRequest(@NonNull GetCredentialRequest request,
             @NonNull OutcomeReceiver<GetCredentialResponse, GetCredentialException> callback) {
         Preconditions.checkNotNull(request, "request must not be null");
-        Preconditions.checkNotNull(callback, "request must not be null");
+        Preconditions.checkNotNull(callback, "callback must not be null");
 
         for (CredentialOption option : request.getCredentialOptions()) {
             ArrayList<AutofillId> ids = option.getCandidateQueryData()
@@ -33899,8 +33899,7 @@
         int category;
         switch (getViewRootImpl().intermittentUpdateState()) {
             case ViewRootImpl.INTERMITTENT_STATE_INTERMITTENT -> category =
-                    (sToolkitFrameRateBySizeReadOnlyFlagValue ? FRAME_RATE_CATEGORY_LOW
-                            : FRAME_RATE_CATEGORY_NORMAL) | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
+                    FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT;
             case ViewRootImpl.INTERMITTENT_STATE_NOT_INTERMITTENT ->
                     category = mSizeBasedFrameRateCategoryAndReason;
             default -> category = mLastFrameRateCategory;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 088d551..ac59b2a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -25,6 +25,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.DragEvent.ACTION_DRAG_LOCATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
 import static android.view.flags.Flags.sensitiveContentPrematureProtectionRemovedFix;
 import static android.view.InputDevice.SOURCE_CLASS_NONE;
 import static android.view.InsetsSource.ID_IME;
@@ -127,6 +128,7 @@
 import static com.android.window.flags.Flags.enableBufferTransformHintFromDisplay;
 import static com.android.window.flags.Flags.setScPropertiesInClient;
 import static com.android.window.flags.Flags.windowSessionRelayoutInfo;
+import static com.android.text.flags.Flags.disableHandwritingInitiatorForIme;
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
@@ -176,6 +178,7 @@
 import android.graphics.RenderNode;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
+import android.hardware.SyncFence;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.display.DisplayManagerGlobal;
@@ -201,6 +204,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.sysprop.DisplayProperties;
+import android.sysprop.ViewProperties;
 import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
@@ -217,6 +221,7 @@
 import android.view.InputDevice.InputSourceClass;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceControl.TransactionStats;
 import android.view.View.AttachInfo;
 import android.view.View.FocusDirection;
 import android.view.View.MeasureSpec;
@@ -267,6 +272,7 @@
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneFallbackEventHandler;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.view.BaseSurfaceHolder;
@@ -291,6 +297,7 @@
 import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -1187,6 +1194,13 @@
     private String mFpsTraceName;
     private String mLargestViewTraceName;
 
+    private final boolean mAppStartInfoTimestampsFlagValue;
+    @GuardedBy("this")
+    private boolean mAppStartTimestampsSent = false;
+    private boolean mAppStartTrackingStarted = false;
+    private long mRenderThreadDrawStartTimeNs = -1;
+    private long mFirstFramePresentedTimeNs = -1;
+
     private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
     private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue;
     private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
@@ -1198,6 +1212,7 @@
             Flags.enableInvalidateCheckThread();
     private static boolean sSurfaceFlingerBugfixFlagValue =
             com.android.graphics.surfaceflinger.flags.Flags.vrrBugfix24q4();
+    private static final boolean sEnableVrr = ViewProperties.vrr_enabled().orElse(true);
 
     static {
         sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
@@ -1303,6 +1318,8 @@
         } else {
             mSensitiveContentProtectionService = null;
         }
+
+        mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps();
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -1561,6 +1578,9 @@
                         pendingInsetsController.replayAndAttach(mInsetsController);
                     }
                 }
+                if (mView instanceof DecorView) {
+                    mWindowAttributes.privateFlags |= PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
+                }
 
                 try {
                     mOrigWindowType = mWindowAttributes.type;
@@ -2575,6 +2595,12 @@
                     notifySurfaceDestroyed();
                 }
                 destroySurface();
+
+                // Reset so they can be sent again for warm starts.
+                mAppStartTimestampsSent = false;
+                mAppStartTrackingStarted = false;
+                mRenderThreadDrawStartTimeNs = -1;
+                mFirstFramePresentedTimeNs = -1;
             }
         }
     }
@@ -4373,6 +4399,30 @@
                 reportDrawFinished(t, seqId);
             }
         });
+
+        // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if
+        // {link mTransactionCompletedTimeNs} has already been set.
+        if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) {
+            mAppStartTrackingStarted = true;
+            Transaction transaction = new Transaction();
+            transaction.addTransactionCompletedListener(mExecutor,
+                    new Consumer<TransactionStats>() {
+                        @Override
+                        public void accept(TransactionStats transactionStats) {
+                            SyncFence presentFence = transactionStats.getPresentFence();
+                            if (presentFence.awaitForever()) {
+                                if (mFirstFramePresentedTimeNs == -1) {
+                                    // Only trigger once per {@link ViewRootImpl} instance.
+                                    mFirstFramePresentedTimeNs = presentFence.getSignalTime();
+                                    maybeSendAppStartTimes();
+                                }
+                            }
+                            presentFence.close();
+                        }
+                    });
+            applyTransactionOnDraw(transaction);
+        }
+
         if (DEBUG_BLAST) {
             Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName());
         }
@@ -4380,6 +4430,45 @@
         mWmsRequestSyncGroup.add(this, null /* runnable */);
     }
 
+    private void maybeSendAppStartTimes() {
+        synchronized (this) {
+            if (mAppStartTimestampsSent) {
+                // Don't send timestamps more than once.
+                return;
+            }
+
+            // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not
+            // post to main thread and check if we have it there.
+            if (mRenderThreadDrawStartTimeNs != -1) {
+                sendAppStartTimesLocked();
+            } else {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        synchronized (ViewRootImpl.this) {
+                            if (mRenderThreadDrawStartTimeNs == -1) {
+                                return;
+                            }
+                            sendAppStartTimesLocked();
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    @GuardedBy("this")
+    private void sendAppStartTimesLocked() {
+        try {
+            ActivityManager.getService().reportStartInfoViewTimestamps(
+                    mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs);
+            mAppStartTimestampsSent = true;
+        } catch (RemoteException e) {
+            // Ignore, timestamps may be lost.
+            if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e);
+        }
+    }
+
     /**
      * Helper used to notify the service to block projection when a sensitive
      * view (the view displays sensitive content) is attached to the window.
@@ -5566,7 +5655,13 @@
                     registerCallbackForPendingTransactions();
                 }
 
+                long timeNs = SystemClock.uptimeNanos();
                 mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
+
+                // Only trigger once per {@link ViewRootImpl} instance.
+                if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) {
+                    mRenderThreadDrawStartTimeNs = timeNs;
+                }
             } else {
                 // If we get here with a disabled & requested hardware renderer, something went
                 // wrong (an invalidate posted right before we destroyed the hardware surface
@@ -7416,6 +7511,8 @@
             final KeyEvent event = (KeyEvent)q.mEvent;
             if (mView.dispatchKeyEventPreIme(event)) {
                 return FINISH_HANDLED;
+            } else if (q.forPreImeOnly()) {
+                return FINISH_NOT_HANDLED;
             }
             return FORWARD;
         }
@@ -7835,7 +7932,11 @@
         private int processPointerEvent(QueuedInputEvent q) {
             final MotionEvent event = (MotionEvent)q.mEvent;
             final int action = event.getAction();
-            boolean handled = mHandwritingInitiator.onTouchEvent(event);
+            boolean handled = false;
+            if (!disableHandwritingInitiatorForIme()
+                    || mWindowAttributes.type != TYPE_INPUT_METHOD) {
+                handled = mHandwritingInitiator.onTouchEvent(event);
+            }
             if (handled) {
                 // If handwriting is started, toolkit doesn't receive ACTION_UP.
                 mLastClickToolType = event.getToolType(event.getActionIndex());
@@ -7987,7 +8088,9 @@
         }
 
         PointerIcon pointerIcon = null;
-        if (event.isStylusPointer() && mIsStylusPointerIconEnabled) {
+        if (event.isStylusPointer() && mIsStylusPointerIconEnabled
+                && (!disableHandwritingInitiatorForIme()
+                        || mWindowAttributes.type != TYPE_INPUT_METHOD)) {
             pointerIcon = mHandwritingInitiator.onResolvePointerIcon(mContext, event);
         }
         if (pointerIcon == null) {
@@ -9906,6 +10009,7 @@
         public static final int FLAG_RESYNTHESIZED = 1 << 4;
         public static final int FLAG_UNHANDLED = 1 << 5;
         public static final int FLAG_MODIFIED_FOR_COMPATIBILITY = 1 << 6;
+        public static final int FLAG_PRE_IME_ONLY = 1 << 7;
 
         public QueuedInputEvent mNext;
 
@@ -9913,6 +10017,13 @@
         public InputEventReceiver mReceiver;
         public int mFlags;
 
+        public boolean forPreImeOnly() {
+            if ((mFlags & FLAG_PRE_IME_ONLY) != 0) {
+                return true;
+            }
+            return false;
+        }
+
         public boolean shouldSkipIme() {
             if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
                 return true;
@@ -9939,6 +10050,7 @@
             hasPrevious = flagToString("FINISHED_HANDLED", FLAG_FINISHED_HANDLED, hasPrevious, sb);
             hasPrevious = flagToString("RESYNTHESIZED", FLAG_RESYNTHESIZED, hasPrevious, sb);
             hasPrevious = flagToString("UNHANDLED", FLAG_UNHANDLED, hasPrevious, sb);
+            hasPrevious = flagToString("FLAG_PRE_IME_ONLY", FLAG_PRE_IME_ONLY, hasPrevious, sb);
             if (!hasPrevious) {
                 sb.append("0");
             }
@@ -9995,7 +10107,7 @@
     }
 
     @UnsupportedAppUsage
-    void enqueueInputEvent(InputEvent event,
+    QueuedInputEvent enqueueInputEvent(InputEvent event,
             InputEventReceiver receiver, int flags, boolean processImmediately) {
         QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
 
@@ -10034,6 +10146,7 @@
         } else {
             scheduleProcessInputEvents();
         }
+        return q;
     }
 
     private void scheduleProcessInputEvents() {
@@ -12355,29 +12468,45 @@
                             + "IWindow:%s Session:%s",
                     mOnBackInvokedDispatcher, mBasePackageName, mWindow, mWindowSession));
         }
-        mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow,
+        mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow, this,
                 mImeBackAnimationController);
     }
 
-    private void sendBackKeyEvent(int action) {
+    /**
+     * Sends {@link KeyEvent#ACTION_DOWN ACTION_DOWN} and {@link KeyEvent#ACTION_UP ACTION_UP}
+     * back key events
+     *
+     * @param preImeOnly whether the back events should be sent to the pre-ime stage only
+     * @return whether the event was handled (i.e. onKeyPreIme consumed it if preImeOnly=true)
+     */
+    public boolean injectBackKeyEvents(boolean preImeOnly) {
+        boolean consumed;
+        try {
+            processingBackKey(true);
+            sendBackKeyEvent(KeyEvent.ACTION_DOWN, preImeOnly);
+            consumed = sendBackKeyEvent(KeyEvent.ACTION_UP, preImeOnly);
+        } finally {
+            processingBackKey(false);
+        }
+        return consumed;
+    }
+
+    private boolean sendBackKeyEvent(int action, boolean preImeOnly) {
         long when = SystemClock.uptimeMillis();
         final KeyEvent ev = new KeyEvent(when, when, action,
                 KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                 InputDevice.SOURCE_KEYBOARD);
-        enqueueInputEvent(ev, null /* receiver */, 0 /* flags */, true /* processImmediately */);
+        int flags = preImeOnly ? QueuedInputEvent.FLAG_PRE_IME_ONLY : 0;
+        QueuedInputEvent q = enqueueInputEvent(ev, null /* receiver */, flags,
+                true /* processImmediately */);
+        return (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
     }
 
     private void registerCompatOnBackInvokedCallback() {
         mCompatOnBackInvokedCallback = () -> {
-            try {
-                processingBackKey(true);
-                sendBackKeyEvent(KeyEvent.ACTION_DOWN);
-                sendBackKeyEvent(KeyEvent.ACTION_UP);
-            } finally {
-                processingBackKey(false);
-            }
+            injectBackKeyEvents(/* preImeOnly */ false);
         };
         if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) {
             Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher");
@@ -12809,8 +12938,13 @@
                                 + mFrameRateCompatibility);
                 }
                 if (sToolkitFrameRateFunctionEnablingReadOnlyFlagValue) {
-                    mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
-                            mFrameRateCompatibility).applyAsyncUnsafe();
+                    if (preferredFrameRate > 0) {
+                        mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
+                                mFrameRateCompatibility);
+                    } else {
+                        mFrameRateTransaction.clearFrameRate(mSurfaceControl);
+                    }
+                    mFrameRateTransaction.applyAsyncUnsafe();
                 }
                 mLastPreferredFrameRate = preferredFrameRate;
             }
@@ -12825,13 +12959,13 @@
 
     private boolean shouldSetFrameRateCategory() {
         // use toolkitSetFrameRate flag to gate the change
-        return  mSurface.isValid() && shouldEnableDvrr();
+        return shouldEnableDvrr() && mSurface.isValid() && shouldEnableDvrr();
     }
 
     private boolean shouldSetFrameRate() {
         // use toolkitSetFrameRate flag to gate the change
-        return mSurface.isValid() && mPreferredFrameRate >= 0
-                && shouldEnableDvrr() && !mIsFrameRateConflicted;
+        return shouldEnableDvrr() && mSurface.isValid() && mPreferredFrameRate >= 0
+                && !mIsFrameRateConflicted;
     }
 
     private boolean shouldTouchBoost(int motionEventAction, int windowType) {
@@ -12866,7 +13000,7 @@
      * @param view The View with the ThreadedRenderer animation that started.
      */
     public void addThreadedRendererView(View view) {
-        if (!mThreadedRendererViews.contains(view)) {
+        if (shouldEnableDvrr() && !mThreadedRendererViews.contains(view)) {
             mThreadedRendererViews.add(view);
         }
     }
@@ -12878,7 +13012,8 @@
      */
     public void removeThreadedRendererView(View view) {
         mThreadedRendererViews.remove(view);
-        if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+        if (shouldEnableDvrr()
+                && !mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
             mInvalidationIdleMessagePosted = true;
             mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
         }
@@ -13099,7 +13234,7 @@
 
     private boolean shouldEnableDvrr() {
         // uncomment this when we are ready for enabling dVRR
-        if (sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
+        if (sEnableVrr && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
             return sToolkitSetFrameRateReadOnlyFlagValue && isFrameRatePowerSavingsBalanced();
         }
         return false;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 1ebced5..52000d9 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2954,6 +2954,15 @@
      * There is a second caption drawn underneath it that will be fast enough. By default the
      * caption is constructed from the theme. You can provide a drawable, that will be drawn instead
      * to better match your application.
+     *
+     * Starting in Android 15, this API is a no-op. New window decorations introduced in Android 14
+     * are drawn in SystemUI process, and OEMs are responsible to make them responsive to resizing.
+     * There is no need to set a background drawable to improve UX anymore since then. Additionally,
+     * the foremost activity can draw in caption areas starting in Android 15. Check
+     * {@link WindowInsetsController#APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND},
+     * {@link WindowInsetsController#APPEARANCE_LIGHT_CAPTION_BARS},
+     * {@link WindowInsetsController#setSystemBarsAppearance(int, int)} and
+     * {@link WindowInsets#getBoundingRects(int)}.
      */
     public abstract void setResizingCaptionDrawable(Drawable drawable);
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 42bf420..ae051f9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -963,7 +963,7 @@
      * true:
      * <ul>
      *     <li>Activity has requested orientation more than two times within one-second timer
-     *     <li>Activity is not letterboxed for fixed orientation
+     *     <li>Activity is not letterboxed for fixed-orientation apps
      * </ul>
      *
      * <p>Setting this property to {@code false} informs the system that the app must be
@@ -1055,22 +1055,22 @@
      * for an app to inform the system that the app should be excluded from the camera compatibility
      * force rotation treatment.
      *
-     * <p>The camera compatibility treatment aligns orientations of portrait app window and natural
-     * orientation of the device and set opposite to natural orientation for a landscape app
-     * window. Mismatch between them can lead to camera issues like sideways or stretched
+     * <p>The camera compatibility treatment aligns portrait app windows with the natural
+     * orientation of the device and landscape app windows opposite the device natural orientation.
+     * Mismatch between the orientations can lead to camera issues like a sideways or stretched
      * viewfinder since this is one of the strongest assumptions that apps make when they implement
-     * camera previews. Since app and natural display orientations aren't guaranteed to match, the
-     * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to
+     * camera previews. Since app and device natural orientations aren't guaranteed to match, the
+     * rotation can cause letterboxing. The forced rotation is triggered as soon as an app opens the
      * camera and is removed once camera is closed.
      *
-     * <p>The camera compatibility can be enabled by device manufacturers on displays that have the
-     * ignore requested orientation display setting enabled (enables compatibility mode for fixed
-     * orientation on Android 12 (API level 31) or higher; see
-     * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced
-     * letterboxing</a> for more details).
+     * <p>Camera compatibility can be enabled by device manufacturers on displays that have the
+     * ignore requested orientation display setting enabled, which enables compatibility mode for
+     * fixed-orientation apps on Android 12 (API level 31) or higher. See
+     * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+     * for more details.
      *
      * <p>With this property set to {@code true} or unset, the system may apply the force rotation
-     * treatment to fixed orientation activities. Device manufacturers can exclude packages from the
+     * treatment to fixed-orientation activities. Device manufacturers can exclude packages from the
      * treatment using their discretion to improve display compatibility.
      *
      * <p>With this property set to {@code false}, the system will not apply the force rotation
@@ -1093,12 +1093,12 @@
      * for an app to inform the system that the app should be excluded from the activity "refresh"
      * after the camera compatibility force rotation treatment.
      *
-     * <p>The camera compatibility treatment aligns orientations of portrait app window and natural
-     * orientation of the device and set opposite to natural orientation for a landscape app
-     * window. Mismatch between them can lead to camera issues like sideways or stretched
+     * <p>The camera compatibility treatment aligns portrait app windows with the natural
+     * orientation of the device and landscape app windows opposite the device natural orientation.
+     * Mismatch between the orientations can lead to camera issues like a sideways or stretched
      * viewfinder since this is one of the strongest assumptions that apps make when they implement
-     * camera previews. Since app and natural display orientations aren't guaranteed to match, the
-     * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to
+     * camera previews. Since app and device natural orientations aren't guaranteed to match, the
+     * rotation can cause letterboxing. The forced rotation is triggered as soon as an app opens the
      * camera and is removed once camera is closed.
      *
      * <p>Force rotation is followed by the "Refresh" of the activity by going through "resumed ->
@@ -1109,10 +1109,10 @@
      * rotation.
      *
      * <p>The camera compatibility can be enabled by device manufacturers on displays that have the
-     * ignore requested orientation display setting enabled (enables compatibility mode for fixed
-     * orientation on Android 12 (API level 31) or higher; see
-     * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced
-     * letterboxing</a> for more details).
+     * ignore requested orientation display setting enabled, which enables compatibility mode for
+     * fixed-orientation apps on Android 12 (API level 31) or higher. See
+     * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+     * for more details.
      *
      * <p>With this property set to {@code true} or unset, the system may "refresh" activity after
      * the force rotation treatment. Device manufacturers can exclude packages from the "refresh"
@@ -1140,12 +1140,11 @@
      * "stopped -> resumed".
      *
      * <p>The camera compatibility treatment aligns orientations of portrait app window and natural
-     * orientation of the device and set opposite to natural orientation for a landscape app
-     * window. Mismatch between them can lead to camera issues like sideways or stretched
-     * viewfinder since this is one of the strongest assumptions that apps make when they implement
-     * camera previews. Since app and natural display orientations aren't guaranteed to match, the
-     * rotation can cause letterboxing. The forced rotation is triggered as soon as app opens to
-     * camera and is removed once camera is closed.
+     * orientation of the device. Mismatch between the orientations can lead to camera issues like a
+     * sideways or stretched viewfinder since this is one of the strongest assumptions that apps
+     * make when they implement camera previews. Since app and natural display orientations aren't
+     * guaranteed to match, the rotation can cause letterboxing. The forced rotation is triggered as
+     * soon as app opens the camera and is removed once camera is closed.
      *
      * <p>Force rotation is followed by the "Refresh" of the activity by going through "resumed ->
      * ... -> stopped -> ... -> resumed" cycle (by default) or "resumed -> paused -> resumed" cycle
@@ -1154,10 +1153,10 @@
      * to sideways or stretching issues persisting even after force rotation.
      *
      * <p>The camera compatibility can be enabled by device manufacturers on displays that have the
-     * ignore requested orientation display setting enabled (enables compatibility mode for fixed
-     * orientation on Android 12 (API level 31) or higher; see
-     * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced
-     * letterboxing</a> for more details).
+     * ignore requested orientation display setting enabled, which enables compatibility mode for
+     * fixed-orientation apps on Android 12 (API level 31) or higher. See
+     * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+     * for more details.
      *
      * <p>Device manufacturers can override packages to "refresh" via "resumed -> paused -> resumed"
      * cycle using their discretion to improve display compatibility.
@@ -1203,7 +1202,7 @@
      * <p>With this property set to {@code true} or unset, device manufacturers can override
      * orientation for the app using their discretion to improve display compatibility.
      *
-     * <p>With this property set to {@code false}, device manufactured per-app override for
+     * <p>With this property set to {@code false}, device manufacturer per-app override for
      * orientation won't be applied.
      *
      * <p><b>Syntax:</b>
@@ -1227,15 +1226,15 @@
      * <p>When this compat override is enabled and while display is fixed to the landscape natural
      * orientation, the orientation requested by the activity will be still respected by bounds
      * resolution logic. For instance, if an activity requests portrait orientation, then activity
-     * will appear in the letterbox mode for fixed orientation with the display rotated to the
-     * lanscape natural orientation.
+     * appears in letterbox mode for fixed-orientation apps with the display rotated to the lanscape
+     * natural orientation.
      *
      * <p>The treatment is disabled by default but device manufacturers can enable the treatment
      * using their discretion to improve display compatibility on displays that have the ignore
-     * orientation request display setting enabled by OEMs on the device (enables compatibility mode
-     * for fixed orientation on Android 12 (API level 31) or higher; see
-     * <a href="https://developer.android.com/guide/practices/enhanced-letterboxing">Enhanced
-     * letterboxing</a> for more details).
+     * orientation request display setting enabled by OEMs on the device, which enables
+     * compatibility mode for fixed-orientation apps on Android 12 (API level 31) or higher. See
+     * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+     * for more details.
      *
      * <p>With this property set to {@code true} or unset, the system wiil use landscape display
      * orientation when the following conditions are met:
@@ -1246,7 +1245,7 @@
      *     <li>Device manufacturer enabled the treatment.
      * </ul>
      *
-     * <p>With this property set to {@code false}, device manufactured per-app override for
+     * <p>With this property set to {@code false}, device manufacturer per-app override for
      * display orientation won't be applied.
      *
      * <p><b>Syntax:</b>
@@ -1344,13 +1343,11 @@
      * see {@link #PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE} to
      * disable the full-screen option only.
      *
-     * <p>The user override is intended to improve the app experience on devices
-     * that have the ignore orientation request display setting enabled by OEMs
-     * (enables compatibility mode for fixed orientation on Android 12 (API
-     * level 31) or higher; see
-     * <a href="https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode">
-     * Large screen compatibility mode</a>
-     * for more details).
+     * <p>The user override is intended to improve the app experience on devices that have the
+     * ignore orientation request display setting enabled by OEMs, which enables compatibility mode
+     * for fixed-orientation apps on Android 12 (API level 31) or higher. See
+     * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+     * for more details.
      *
      * <p>To opt out of the user aspect ratio compatibility override, add this property
      * to your app manifest and set the value to {@code false}. Your app will be excluded
@@ -1383,13 +1380,11 @@
      * <p>When users apply the full-screen compatibility override, the orientation
      * of the activity is forced to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER}.
      *
-     * <p>The user override is intended to improve the app experience on devices
-     * that have the ignore orientation request display setting enabled by OEMs
-     * (enables compatibility mode for fixed orientation on Android 12 (API
-     * level 31) or higher; see
-     * <a href="https://developer.android.com/guide/topics/large-screens/large-screen-compatibility-mode">
-     * Large screen compatibility mode</a>
-     * for more details).
+     * <p>The user override is intended to improve the app experience on devices that have the
+     * ignore orientation request display setting enabled by OEMs, which enables compatibility mode
+     * for fixed-orientation apps on Android 12 (API level 31) or higher. See
+     * <a href="{@docRoot}guide/practices/device-compatibility-mode">Device compatibility mode</a>
+     * for more details.
      *
      * <p>To opt out of the full-screen option of the user aspect ratio compatibility
      * override, add this property to your app manifest and set the value to {@code false}.
@@ -3312,6 +3307,11 @@
         @UnsupportedAppUsage
         public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 1 << 6;
 
+        /** Window flag: the client side view can intercept back progress, so system does not
+         * need to pilfer pointers.
+         * {@hide} */
+        public static final int PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED = 1 << 7;
+
         /** Window flag: a special option intended for system dialogs.  When
          * this flag is set, the window will demand focus unconditionally when
          * it is created.
@@ -3505,6 +3505,7 @@
                 SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
                 PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION,
                 PRIVATE_FLAG_NO_MOVE_ANIMATION,
+                PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED,
                 PRIVATE_FLAG_SYSTEM_ERROR,
                 PRIVATE_FLAG_OPTIMIZE_MEASURE,
                 PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS,
diff --git a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
index 1fe8180..12e0814 100644
--- a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
+++ b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
@@ -302,10 +302,6 @@
                 }
 
                 @Override
-                public void onMagnificationSystemUIConnectionChanged(boolean connected) {
-                }
-
-                @Override
                 public void onMagnificationChanged(int displayId, @NonNull Region region,
                         MagnificationConfig config) {
                 }
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 749f977..c92593f 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -97,7 +97,7 @@
     /**
      * Window type: A system window that has the function to control an associated window.
      */
-    @FlaggedApi(Flags.FLAG_ADD_TYPE_WINDOW_CONTROL)
+    @FlaggedApi(Flags.FLAG_ENABLE_TYPE_WINDOW_CONTROL)
     public static final int TYPE_WINDOW_CONTROL = 7;
 
     /* Special values for window IDs */
@@ -880,7 +880,7 @@
      * @hide
      */
     public static String typeToString(int type) {
-        if (Flags.addTypeWindowControl() && type == TYPE_WINDOW_CONTROL) {
+        if (Flags.enableTypeWindowControl() && type == TYPE_WINDOW_CONTROL) {
             return "TYPE_WINDOW_CONTROL";
         }
 
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index edf3387..95d001f 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -122,9 +122,8 @@
 
 flag {
     namespace: "accessibility"
-    name: "add_type_window_control"
+    name: "enable_type_window_control"
     is_exported: true
-    is_fixed_read_only: true
     description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations"
     bug: "320445550"
 }
@@ -169,13 +168,3 @@
     description: "Feature flag for declaring system pinch zoom opt-out apis"
     bug: "315089687"
 }
-
-flag {
-    name: "wait_magnification_system_ui_connection_to_notify_service_connected"
-    namespace: "accessibility"
-    description: "Decide whether AccessibilityService needs to wait until magnification system ui connection is ready to trigger onServiceConnected"
-    bug: "337800504"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 46b41ae..199a69a 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -524,7 +524,7 @@
         return DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_AUTOFILL,
                 DEVICE_CONFIG_INCLUDE_INVISIBLE_VIEW_GROUP_IN_ASSIST_STRUCTURE,
-                false);
+                true);
     }
 
     /** @hide */
@@ -548,7 +548,7 @@
         return DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_AUTOFILL,
                 DEVICE_CONFIG_FILL_FIELDS_FROM_CURRENT_SESSION_ONLY,
-                false);
+                true);
     }
 
     /**
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 15ba1a1..6b60858 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -61,6 +61,11 @@
     }
 
     /** @hide */
+    public AutofillId(@NonNull AutofillId hostId, int virtualChildId, int sessionId) {
+        this(FLAG_IS_VIRTUAL_INT | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId);
+    }
+
+    /** @hide */
     @TestApi
     public AutofillId(@NonNull AutofillId hostId, long virtualChildId, int sessionId) {
         this(FLAG_IS_VIRTUAL_LONG | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId);
@@ -236,9 +241,9 @@
     public String toString() {
         final StringBuilder builder = new StringBuilder().append(mViewId);
         if (isVirtualInt()) {
-            builder.append(':').append(mVirtualIntId);
+            builder.append(":i").append(mVirtualIntId);
         } else if (isVirtualLong()) {
-            builder.append(':').append(mVirtualLongId);
+            builder.append(":l").append(mVirtualLongId);
         }
 
         if (hasSession()) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9cc4191..0d4c556 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -193,6 +193,7 @@
 @RequiresFeature(PackageManager.FEATURE_AUTOFILL)
 public final class AutofillManager {
 
+    private static final boolean DBG = false;
     private static final String TAG = "AutofillManager";
 
     /**
@@ -2027,7 +2028,31 @@
         if (!hasAutofillFeature()) {
             return;
         }
+        if (DBG) {
+            Log.v(TAG, "notifyValueChanged() called with virtualId:" + virtualId + " value:"
+                    + value);
+        }
         synchronized (mLock) {
+            if (mLastAutofilledData != null) {
+                AutofillId id = new AutofillId(view.getAutofillId(), virtualId, mSessionId);
+                if (mLastAutofilledData.containsKey(id)) {
+                    if (Objects.equals(mLastAutofilledData.get(id), value)) {
+                        // Indicates that the view was autofilled
+                        if (sDebug) {
+                            Log.v(TAG, "notifyValueChanged() virtual view autofilled successfully:"
+                                    + virtualId + " value:" + value);
+                        }
+                        try {
+                            mService.setViewAutofilled(mSessionId, id, mContext.getUserId());
+                        } catch (RemoteException e) {
+                            // The failure could be a consequence of something going wrong on the
+                            // server side. Do nothing here since it's just logging, but it's
+                            // possible follow-up actions may fail.
+                            Log.w(TAG, "RemoteException caught but ignored " + e);
+                        }
+                    }
+                }
+            }
             if (!mEnabled || !isActiveLocked()) {
                 if (sVerbose) {
                     Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
@@ -2985,16 +3010,34 @@
                 mLastAutofilledData.put(view.getAutofillId(), targetValue);
             }
             view.setAutofilled(true, hideHighlight);
+            if (sDebug) {
+                Log.d(TAG, "View " + view.getAutofillId() + " autofilled synchronously.");
+            }
             try {
                 mService.setViewAutofilled(mSessionId, view.getAutofillId(), mContext.getUserId());
             } catch (RemoteException e) {
                 // The failure could be a consequence of something going wrong on the server side.
                 // Do nothing here since it's just logging, but it's possible follow-up actions may
                 // fail.
+                Log.w(TAG, "Unable to log due to " + e);
+            }
+        } else {
+            if (sDebug) {
+                Log.d(TAG, "View " + view.getAutofillId() + " " + view.getClass().toString()
+                        + " from " + view.getClass().getPackageName()
+                        + " : didn't fill in synchronously. It may fill asynchronously.");
             }
         }
     }
 
+    /**
+     * Returns String with text "null" if the object is null, or the actual string represented by
+     * the object.
+     */
+    private @NonNull String getString(Object obj) {
+        return obj == null ? "null" : obj.toString();
+    }
+
     private void onGetCredentialException(int sessionId, AutofillId id, String errorType,
             String errorMsg) {
         synchronized (mLock) {
@@ -3114,6 +3157,10 @@
 
             ArrayList<AutofillId> failedIds = new ArrayList<>();
 
+            if (mLastAutofilledData == null) {
+                mLastAutofilledData = new ParcelableMap(itemCount);
+            }
+
             for (int i = 0; i < itemCount; i++) {
                 final AutofillId id = ids.get(i);
                 final AutofillValue value = values.get(i);
@@ -3126,6 +3173,9 @@
                     failedIds.add(id);
                     continue;
                 }
+                // Mark the view as to be autofilled with 'value'
+                mLastAutofilledData.put(id, value);
+
                 if (id.isVirtualInt()) {
                     if (virtualValues == null) {
                         // Most likely there will be just one view with virtual children.
@@ -3139,12 +3189,6 @@
                     }
                     valuesByParent.put(id.getVirtualChildIntId(), value);
                 } else {
-                    // Mark the view as to be autofilled with 'value'
-                    if (mLastAutofilledData == null) {
-                        mLastAutofilledData = new ParcelableMap(itemCount - i);
-                    }
-                    mLastAutofilledData.put(id, value);
-
                     view.autofill(value);
 
                     // Set as autofilled if the values match now, e.g. when the value was updated
@@ -3575,40 +3619,14 @@
         // isCredential field indicates that the developer might be calling Credman, and we should
         // suppress autofill dialogs. But it is not a good enough indicator that there is a valid
         // credman option.
-        if (view.isCredential()) {
-            return true;
-        }
-        return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER);
+        return view.isCredential() || isCredmanRequested(view);
     }
 
     private boolean isCredmanRequested(View view) {
         if (view == null) {
             return false;
         }
-        if (view.getViewCredentialHandler() != null) {
-            return true;
-        }
-
-        String[] hints = view.getAutofillHints();
-        if (hints == null) {
-            return false;
-        }
-        // if hint starts with 'credential=', then we assume that there is a valid
-        // credential option set by the client.
-        return containsAutofillHintPrefix(view, View.AUTOFILL_HINT_CREDENTIAL_MANAGER + "=");
-    }
-
-    private boolean containsAutofillHintPrefix(View view, String prefix) {
-        String[] hints = view.getAutofillHints();
-        if (hints == null) {
-            return false;
-        }
-        for (String hint : hints) {
-            if (hint != null && hint.startsWith(prefix)) {
-                return true;
-            }
-        }
-        return false;
+        return view.getViewCredentialHandler() != null;
     }
 
     /**
@@ -4308,14 +4326,16 @@
 
             if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
                 if (sVerbose) {
-                    Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleTrackedIds);
+                    Log.v(TAG, "No more visible tracked save ids. Invisible = "
+                            + mInvisibleTrackedIds);
                 }
                 finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
 
             }
             if (mVisibleDialogTrackedIds.isEmpty()) {
                 if (sVerbose) {
-                    Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleDialogTrackedIds);
+                    Log.v(TAG, "No more visible tracked fill dialog ids. Invisible = "
+                            + mInvisibleDialogTrackedIds);
                 }
                 processNoVisibleTrackedAllViews();
             }
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index bcef37f..724e8fa 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -18,7 +18,6 @@
 import static android.view.contentcapture.ContentCaptureHelper.sDebug;
 import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
 import static android.view.contentcapture.ContentCaptureHelper.toSet;
-import static android.view.contentcapture.flags.Flags.runOnBackgroundThreadEnabled;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -366,6 +365,14 @@
             "enable_content_protection_receiver";
 
     /**
+     * Whether AssistContent snapshot should be sent on activity start.
+     *
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT =
+            "enable_activity_start_assist_content";
+
+    /**
      * Sets the size of the in-memory ring buffer for the content protection flow.
      *
      * @hide
@@ -594,26 +601,16 @@
     public ContentCaptureSession getMainContentCaptureSession() {
         synchronized (mLock) {
             if (mMainSession == null) {
-                mMainSession = prepareMainSession();
-                if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
-            }
-            return mMainSession;
-        }
-    }
-
-    @NonNull
-    @GuardedBy("mLock")
-    private ContentCaptureSession prepareMainSession() {
-        if (runOnBackgroundThreadEnabled()) {
-            return new MainContentCaptureSessionV2(
+                mMainSession = new MainContentCaptureSession(
                     mContext,
                     this,
                     prepareUiHandler(),
                     prepareContentCaptureHandler(),
                     mService
-            );
-        } else {
-            return new MainContentCaptureSession(mContext, this, prepareUiHandler(), mService);
+                );
+                if (sVerbose) Log.v(TAG, "getMainContentCaptureSession(): created " + mMainSession);
+            }
+            return mMainSession;
         }
     }
 
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index a90c94e..eb827dd 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -69,16 +69,13 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
-// TODO(b/309411951): Replace V2 as the only main session once the experiment is done.
 /**
  * Main session associated with a context.
  *
- * <p>This session is created when the activity starts and finished when it stops; clients can use
- * it to create children activities.
- *
  * @hide
  */
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -107,7 +104,10 @@
     private final ContentCaptureManager mManager;
 
     @NonNull
-    private final Handler mHandler;
+    private final Handler mUiHandler;
+
+    @NonNull
+    private final Handler mContentCaptureHandler;
 
     /**
      * Interface to the system_server binder object - it's only used to start the session (and
@@ -142,6 +142,18 @@
     public ComponentName mComponentName;
 
     /**
+     * Thread-safe queue of events held to be processed as a batch.
+     *
+     * Because it is not guaranteed that the events will be enqueued from a single thread, the
+     * implementation must be thread-safe to prevent unexpected behaviour.
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @NonNull
+    public final ConcurrentLinkedQueue<ContentCaptureEvent> mEventProcessQueue;
+
+    /**
      * List of events held to be sent to the {@link ContentCaptureService} as a batch.
      *
      * @hide
@@ -200,14 +212,14 @@
                 binder = resultData.getBinder(EXTRA_BINDER);
                 if (binder == null) {
                     Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
-                    mainSession.mHandler.post(() -> mainSession.resetSession(
+                    mainSession.runOnContentCaptureThread(() -> mainSession.resetSession(
                             STATE_DISABLED | STATE_INTERNAL_ERROR));
                     return;
                 }
             } else {
                 binder = null;
             }
-            mainSession.mHandler.post(() ->
+            mainSession.runOnContentCaptureThread(() ->
                     mainSession.onSessionStarted(resultCode, binder));
         }
     }
@@ -217,17 +229,21 @@
     public MainContentCaptureSession(
             @NonNull ContentCaptureManager.StrippedContext context,
             @NonNull ContentCaptureManager manager,
-            @NonNull Handler handler,
+            @NonNull Handler uiHandler,
+            @NonNull Handler contentCaptureHandler,
             @NonNull IContentCaptureManager systemServerInterface) {
         mContext = context;
         mManager = manager;
-        mHandler = handler;
+        mUiHandler = uiHandler;
+        mContentCaptureHandler = contentCaptureHandler;
         mSystemServerInterface = systemServerInterface;
 
         final int logHistorySize = mManager.mOptions.logHistorySize;
         mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
 
         mSessionStateReceiver = new SessionStateReceiver(this);
+
+        mEventProcessQueue = new ConcurrentLinkedQueue<>();
     }
 
     @Override
@@ -248,7 +264,13 @@
     @Override
     void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
             @NonNull ComponentName component, int flags) {
-        checkOnUiThread();
+        runOnContentCaptureThread(
+                () -> startImpl(token, shareableActivityToken, component, flags));
+    }
+
+    private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+               @NonNull ComponentName component, int flags) {
+        checkOnContentCaptureThread();
         if (!isContentCaptureEnabled()) return;
 
         if (sVerbose) {
@@ -282,17 +304,15 @@
             Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
         }
     }
-
     @Override
     void onDestroy() {
-        mHandler.removeMessages(MSG_FLUSH);
-        mHandler.post(() -> {
+        clearAndRunOnContentCaptureThread(() -> {
             try {
                 flush(FLUSH_REASON_SESSION_FINISHED);
             } finally {
                 destroySession();
             }
-        });
+        }, MSG_FLUSH);
     }
 
     /**
@@ -305,7 +325,7 @@
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void onSessionStarted(int resultCode, @Nullable IBinder binder) {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         if (binder != null) {
             mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
             mDirectServiceVulture = () -> {
@@ -324,7 +344,7 @@
             mContentProtectionEventProcessor =
                     new ContentProtectionEventProcessor(
                             mManager.getContentProtectionEventBuffer(),
-                            mHandler,
+                            mContentCaptureHandler,
                             mSystemServerInterface,
                             mComponentName.getPackageName(),
                             mManager.mOptions.contentProtectionOptions);
@@ -354,7 +374,7 @@
     }
 
     private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         final int eventType = event.getType();
         if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
         if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
@@ -398,14 +418,14 @@
     }
 
     private void sendContentProtectionEvent(@NonNull ContentCaptureEvent event) {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         if (mContentProtectionEventProcessor != null) {
             mContentProtectionEventProcessor.processEvent(event);
         }
     }
 
     private void sendContentCaptureEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         final int eventType = event.getType();
         final int maxBufferSize = mManager.mOptions.maxBufferSize;
         if (mEvents == null) {
@@ -540,12 +560,12 @@
     }
 
     private boolean hasStarted() {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         return mState != UNKNOWN_STATE;
     }
 
     private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         if (sVerbose) {
             Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
                     + ", checkExisting=" + checkExisting);
@@ -562,9 +582,9 @@
                     + "when disabled. events=" + (mEvents == null ? null : mEvents.size()));
             return;
         }
-        if (checkExisting && mHandler.hasMessages(MSG_FLUSH)) {
+        if (checkExisting && mContentCaptureHandler.hasMessages(MSG_FLUSH)) {
             // "Renew" the flush message by removing the previous one
-            mHandler.removeMessages(MSG_FLUSH);
+            mContentCaptureHandler.removeMessages(MSG_FLUSH);
         }
 
         final int flushFrequencyMs;
@@ -586,12 +606,12 @@
                     + flushFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
         }
         // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
-        mHandler.postDelayed(() ->
+        mContentCaptureHandler.postDelayed(() ->
                 flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
     }
 
     private void flushIfNeeded(@FlushReason int reason) {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         if (mEvents == null || mEvents.isEmpty()) {
             if (sVerbose) Log.v(TAG, "Nothing to flush");
             return;
@@ -603,7 +623,11 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     @Override
     public void flush(@FlushReason int reason) {
-        checkOnUiThread();
+        runOnContentCaptureThread(() -> flushImpl(reason));
+    }
+
+    private void flushImpl(@FlushReason int reason) {
+        checkOnContentCaptureThread();
         if (mEvents == null || mEvents.size() == 0) {
             if (sVerbose) {
                 Log.v(TAG, "Don't flush for empty event buffer.");
@@ -626,7 +650,7 @@
                 Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
                         + "client not ready: " + mEvents);
             }
-            if (!mHandler.hasMessages(MSG_FLUSH)) {
+            if (!mContentCaptureHandler.hasMessages(MSG_FLUSH)) {
                 scheduleFlush(reason, /* checkExisting= */ false);
             }
             return;
@@ -652,7 +676,7 @@
             mFlushHistory.log(logRecord);
         }
         try {
-            mHandler.removeMessages(MSG_FLUSH);
+            mContentCaptureHandler.removeMessages(MSG_FLUSH);
 
             final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
             mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
@@ -672,7 +696,7 @@
      */
     @NonNull
     private ParceledListSlice<ContentCaptureEvent> clearEvents() {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         // NOTE: we must save a reference to the current mEvents and then set it to to null,
         // otherwise clearing it would clear it in the receiving side if the service is also local.
         if (mEvents == null) {
@@ -687,7 +711,7 @@
     /** hide */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void destroySession() {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         if (sDebug) {
             Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
                     + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
@@ -707,6 +731,7 @@
         }
         mDirectServiceInterface = null;
         mContentProtectionEventProcessor = null;
+        mEventProcessQueue.clear();
     }
 
     // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
@@ -714,7 +739,7 @@
     /** @hide */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void resetSession(int newState) {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         if (sVerbose) {
             Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
                     + getStateAsString(mState) + " to " + getStateAsString(newState));
@@ -735,21 +760,21 @@
         }
         mDirectServiceInterface = null;
         mContentProtectionEventProcessor = null;
-        mHandler.removeMessages(MSG_FLUSH);
+        mContentCaptureHandler.removeMessages(MSG_FLUSH);
     }
 
     @Override
     void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
         final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
                 .setViewNode(node.mNode);
-        mHandler.post(() -> sendEvent(event));
+        enqueueEvent(event);
     }
 
     @Override
     void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
         final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED)
                 .setAutofillId(id);
-        mHandler.post(() -> sendEvent(event));
+        enqueueEvent(event);
     }
 
     @Override
@@ -780,7 +805,7 @@
                 .setAutofillId(id).setText(eventText)
                 .setComposingIndex(composingStart, composingEnd)
                 .setSelectionIndex(startIndex, endIndex);
-        mHandler.post(() -> sendEvent(event));
+        enqueueEvent(event);
     }
 
     @Override
@@ -788,7 +813,7 @@
         final ContentCaptureEvent event =
                 new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
                         .setInsets(viewInsets);
-        mHandler.post(() -> sendEvent(event));
+        enqueueEvent(event);
     }
 
     @Override
@@ -798,19 +823,19 @@
         final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH;
 
         final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type);
-        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+        enqueueEvent(event, forceFlush);
     }
 
     @Override
     public void internalNotifySessionResumed() {
         final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_RESUMED);
-        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+        enqueueEvent(event, FORCE_FLUSH);
     }
 
     @Override
     public void internalNotifySessionPaused() {
         final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_PAUSED);
-        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+        enqueueEvent(event, FORCE_FLUSH);
     }
 
     @Override
@@ -818,12 +843,16 @@
         return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
     }
 
-    @Override
+    // Called by ContentCaptureManager.isContentCaptureEnabled
     boolean isDisabled() {
         return mDisabled.get();
     }
 
-    @Override
+    /**
+     * Sets the disabled state of content capture.
+     *
+     * @return whether disabled state was changed.
+     */
     boolean setDisabled(boolean disabled) {
         return mDisabled.compareAndSet(!disabled, disabled);
     }
@@ -835,7 +864,7 @@
                 new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
                         .setParentSessionId(parentSessionId)
                         .setClientContext(clientContext);
-        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+        enqueueEvent(event, FORCE_FLUSH);
     }
 
     @Override
@@ -843,14 +872,14 @@
         final ContentCaptureEvent event =
                 new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
                         .setParentSessionId(parentSessionId);
-        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+        enqueueEvent(event, FORCE_FLUSH);
     }
 
     @Override
     void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
         final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
                 .setClientContext(context);
-        mHandler.post(() -> sendEvent(event, FORCE_FLUSH));
+        enqueueEvent(event, FORCE_FLUSH);
     }
 
     @Override
@@ -858,18 +887,97 @@
         final ContentCaptureEvent event =
                 new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
                         .setBounds(bounds);
-        mHandler.post(() -> sendEvent(event));
+        enqueueEvent(event);
+    }
+
+    private List<ContentCaptureEvent> clearBufferEvents() {
+        final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>();
+        ContentCaptureEvent event;
+        while ((event = mEventProcessQueue.poll()) != null) {
+            bufferEvents.add(event);
+        }
+        return bufferEvents;
+    }
+
+    private void enqueueEvent(@NonNull final ContentCaptureEvent event) {
+        enqueueEvent(event, /* forceFlush */ false);
+    }
+
+    /**
+     * Enqueue the event into {@code mEventProcessBuffer} if it is not an urgent request. Otherwise,
+     * clear the buffer events then starting sending out current event.
+     */
+    private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
+        if (forceFlush || mEventProcessQueue.size() >= mManager.mOptions.maxBufferSize - 1) {
+            // The buffer events are cleared in the same thread first to prevent new events
+            // being added during the time of context switch. This would disrupt the sequence
+            // of events.
+            final List<ContentCaptureEvent> batchEvents = clearBufferEvents();
+            runOnContentCaptureThread(() -> {
+                for (int i = 0; i < batchEvents.size(); i++) {
+                    sendEvent(batchEvents.get(i));
+                }
+                sendEvent(event, /* forceFlush= */ true);
+            });
+        } else {
+            mEventProcessQueue.offer(event);
+        }
     }
 
     @Override
     public void notifyContentCaptureEvents(
             @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
-        notifyContentCaptureEventsImpl(contentCaptureEvents);
+        runOnUiThread(() -> {
+            prepareViewStructures(contentCaptureEvents);
+            runOnContentCaptureThread(() ->
+                    notifyContentCaptureEventsImpl(contentCaptureEvents));
+        });
+    }
+
+    /**
+     * Traverse events and pre-process {@link View} events to {@link ViewStructureSession} events.
+     * If a {@link View} event is invalid, an empty {@link ViewStructureSession} will still be
+     * provided.
+     */
+    private void prepareViewStructures(
+            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+        for (int i = 0; i < contentCaptureEvents.size(); i++) {
+            int sessionId = contentCaptureEvents.keyAt(i);
+            ArrayList<Object> events = contentCaptureEvents.valueAt(i);
+            for_each_event: for (int j = 0; j < events.size(); j++) {
+                Object event = events.get(j);
+                if (event instanceof View) {
+                    View view = (View) event;
+                    ContentCaptureSession session = view.getContentCaptureSession();
+                    ViewStructureSession structureSession = new ViewStructureSession();
+
+                    // Replace the View event with ViewStructureSession no matter the data is
+                    // available or not. This is to ensure the sequence of the events are still
+                    // the same. Calls to notifyViewAppeared will check the availability later.
+                    events.set(j, structureSession);
+                    if (session == null) {
+                        Log.w(TAG, "no content capture session on view: " + view);
+                        continue for_each_event;
+                    }
+                    int actualId = session.getId();
+                    if (actualId != sessionId) {
+                        Log.w(TAG, "content capture session mismatch for view (" + view
+                                + "): was " + sessionId + " before, it's " + actualId + " now");
+                        continue for_each_event;
+                    }
+                    ViewStructure structure = session.newViewStructure(view);
+                    view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
+
+                    structureSession.setSession(session);
+                    structureSession.setStructure(structure);
+                }
+            }
+        }
     }
 
     private void notifyContentCaptureEventsImpl(
             @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
-        checkOnUiThread();
+        checkOnContentCaptureThread();
         try {
             if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
@@ -882,22 +990,8 @@
                     Object event = events.get(j);
                     if (event instanceof AutofillId) {
                         internalNotifyViewDisappeared(sessionId, (AutofillId) event);
-                    } else if (event instanceof View) {
-                        View view = (View) event;
-                        ContentCaptureSession session = view.getContentCaptureSession();
-                        if (session == null) {
-                            Log.w(TAG, "no content capture session on view: " + view);
-                            continue for_each_event;
-                        }
-                        int actualId = session.getId();
-                        if (actualId != sessionId) {
-                            Log.w(TAG, "content capture session mismatch for view (" + view
-                                    + "): was " + sessionId + " before, it's " + actualId + " now");
-                            continue for_each_event;
-                        }
-                        ViewStructure structure = session.newViewStructure(view);
-                        view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
-                        session.notifyViewAppeared(structure);
+                    } else if (event instanceof ViewStructureSession viewStructureSession) {
+                        viewStructureSession.notifyViewAppeared();
                     } else if (event instanceof Insets) {
                         internalNotifyViewInsetsChanged(sessionId, (Insets) event);
                     } else {
@@ -1015,9 +1109,9 @@
      * Therefore, accessing internal properties in {@link MainContentCaptureSession} should
      * always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
      */
-    private void checkOnUiThread() {
-        final boolean onUiThread = mHandler.getLooper().isCurrentThread();
-        if (!onUiThread) {
+    private void checkOnContentCaptureThread() {
+        final boolean onContentCaptureThread = mContentCaptureHandler.getLooper().isCurrentThread();
+        if (!onContentCaptureThread) {
             mWrongThreadCount.incrementAndGet();
             Log.e(TAG, "MainContentCaptureSession running on " + Thread.currentThread());
         }
@@ -1028,4 +1122,63 @@
         Counter.logIncrement(
                 CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID, mWrongThreadCount.getAndSet(0));
     }
+
+    /**
+     * Ensures that {@code r} will be running on the assigned thread.
+     *
+     * <p>This is to prevent unnecessary delegation to Handler that results in fragmented runnable.
+     * </p>
+     */
+    private void runOnContentCaptureThread(@NonNull Runnable r) {
+        if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
+            mContentCaptureHandler.post(r);
+        } else {
+            r.run();
+        }
+    }
+
+    private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
+        if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
+            mContentCaptureHandler.removeMessages(what);
+            mContentCaptureHandler.post(r);
+        } else {
+            r.run();
+        }
+    }
+
+    private void runOnUiThread(@NonNull Runnable r) {
+        if (mUiHandler.getLooper().isCurrentThread()) {
+            r.run();
+        } else {
+            mUiHandler.post(r);
+        }
+    }
+
+    /**
+     * Holds {@link ContentCaptureSession} and related {@link ViewStructure} for processing.
+     */
+    private static final class ViewStructureSession {
+        @Nullable private ContentCaptureSession mSession;
+        @Nullable private ViewStructure mStructure;
+
+        ViewStructureSession() {}
+
+        void setSession(@Nullable ContentCaptureSession session) {
+            this.mSession = session;
+        }
+
+        void setStructure(@Nullable ViewStructure struct) {
+            this.mStructure = struct;
+        }
+
+        /**
+         * Calls {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)} if the session and
+         * the view structure are available.
+         */
+        void notifyViewAppeared() {
+            if (mSession != null && mStructure != null) {
+                mSession.notifyViewAppeared(mStructure);
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java b/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java
deleted file mode 100644
index fbb66d1..0000000
--- a/core/java/android/view/contentcapture/MainContentCaptureSessionV2.java
+++ /dev/null
@@ -1,1187 +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 android.view.contentcapture;
-
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_INSETS_CHANGED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING;
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_WINDOW_BOUNDS_CHANGED;
-import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
-import static android.view.contentcapture.ContentCaptureHelper.sDebug;
-import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
-import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.pm.ParceledListSlice;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import android.os.Trace;
-import android.service.contentcapture.ContentCaptureService;
-import android.text.Selection;
-import android.text.Spannable;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.TimeUtils;
-import android.view.View;
-import android.view.ViewStructure;
-import android.view.autofill.AutofillId;
-import android.view.contentcapture.ViewNode.ViewStructureImpl;
-import android.view.contentprotection.ContentProtectionEventProcessor;
-import android.view.inputmethod.BaseInputConnection;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.IResultReceiver;
-import com.android.modules.expresslog.Counter;
-
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Main session associated with a context.
- *
- * <p>This is forked from {@link MainContentCaptureSession} to hold the logic of running operations
- * in the background thread.</p>
- *
- * @hide
- */
-@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-public final class MainContentCaptureSessionV2 extends ContentCaptureSession {
-
-    private static final String TAG = MainContentCaptureSession.class.getSimpleName();
-
-    private static final String CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID =
-            "content_capture.value_content_capture_wrong_thread_count";
-
-    // For readability purposes...
-    private static final boolean FORCE_FLUSH = true;
-
-    /**
-     * Handler message used to flush the buffer.
-     */
-    private static final int MSG_FLUSH = 1;
-
-    @NonNull
-    private final AtomicBoolean mDisabled = new AtomicBoolean(false);
-
-    @NonNull
-    private final ContentCaptureManager.StrippedContext mContext;
-
-    @NonNull
-    private final ContentCaptureManager mManager;
-
-    @NonNull
-    private final Handler mUiHandler;
-
-    @NonNull
-    private final Handler mContentCaptureHandler;
-
-    /**
-     * Interface to the system_server binder object - it's only used to start the session (and
-     * notify when the session is finished).
-     */
-    @NonNull
-    private final IContentCaptureManager mSystemServerInterface;
-
-    /**
-     * Direct interface to the service binder object - it's used to send the events, including the
-     * last ones (when the session is finished)
-     *
-     * @hide
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    @Nullable
-    public IContentCaptureDirectManager mDirectServiceInterface;
-
-    @Nullable
-    private DeathRecipient mDirectServiceVulture;
-
-    private int mState = UNKNOWN_STATE;
-
-    @Nullable
-    private IBinder mApplicationToken;
-    @Nullable
-    private IBinder mShareableActivityToken;
-
-    /** @hide */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    @Nullable
-    public ComponentName mComponentName;
-
-    /**
-     * Thread-safe queue of events held to be processed as a batch.
-     *
-     * Because it is not guaranteed that the events will be enqueued from a single thread, the
-     * implementation must be thread-safe to prevent unexpected behaviour.
-     *
-     * @hide
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    @NonNull
-    public final ConcurrentLinkedQueue<ContentCaptureEvent> mEventProcessQueue;
-
-    /**
-     * List of events held to be sent to the {@link ContentCaptureService} as a batch.
-     *
-     * @hide
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    @Nullable
-    public ArrayList<ContentCaptureEvent> mEvents;
-
-    // Used just for debugging purposes (on dump)
-    private long mNextFlush;
-
-    /**
-     * Whether the next buffer flush is queued by a text changed event.
-     */
-    private boolean mNextFlushForTextChanged = false;
-
-    @Nullable
-    private final LocalLog mFlushHistory;
-
-    private final AtomicInteger mWrongThreadCount = new AtomicInteger(0);
-
-    /**
-     * Binder object used to update the session state.
-     */
-    @NonNull
-    private final SessionStateReceiver mSessionStateReceiver;
-
-    /** @hide */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    @Nullable
-    public ContentProtectionEventProcessor mContentProtectionEventProcessor;
-
-    private static class SessionStateReceiver extends IResultReceiver.Stub {
-        private final WeakReference<MainContentCaptureSessionV2> mMainSession;
-
-        SessionStateReceiver(MainContentCaptureSessionV2 session) {
-            mMainSession = new WeakReference<>(session);
-        }
-
-        @Override
-        public void send(int resultCode, Bundle resultData) {
-            final MainContentCaptureSessionV2 mainSession = mMainSession.get();
-            if (mainSession == null) {
-                Log.w(TAG, "received result after mina session released");
-                return;
-            }
-            final IBinder binder;
-            if (resultData != null) {
-                // Change in content capture enabled.
-                final boolean hasEnabled = resultData.getBoolean(EXTRA_ENABLED_STATE);
-                if (hasEnabled) {
-                    final boolean disabled = (resultCode == RESULT_CODE_FALSE);
-                    mainSession.mDisabled.set(disabled);
-                    return;
-                }
-                binder = resultData.getBinder(EXTRA_BINDER);
-                if (binder == null) {
-                    Log.wtf(TAG, "No " + EXTRA_BINDER + " extra result");
-                    mainSession.runOnContentCaptureThread(() -> mainSession.resetSession(
-                            STATE_DISABLED | STATE_INTERNAL_ERROR));
-                    return;
-                }
-            } else {
-                binder = null;
-            }
-            mainSession.runOnContentCaptureThread(() ->
-                    mainSession.onSessionStarted(resultCode, binder));
-        }
-    }
-
-    /** @hide */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
-    public MainContentCaptureSessionV2(
-            @NonNull ContentCaptureManager.StrippedContext context,
-            @NonNull ContentCaptureManager manager,
-            @NonNull Handler uiHandler,
-            @NonNull Handler contentCaptureHandler,
-            @NonNull IContentCaptureManager systemServerInterface) {
-        mContext = context;
-        mManager = manager;
-        mUiHandler = uiHandler;
-        mContentCaptureHandler = contentCaptureHandler;
-        mSystemServerInterface = systemServerInterface;
-
-        final int logHistorySize = mManager.mOptions.logHistorySize;
-        mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null;
-
-        mSessionStateReceiver = new SessionStateReceiver(this);
-
-        mEventProcessQueue = new ConcurrentLinkedQueue<>();
-    }
-
-    @Override
-    ContentCaptureSession getMainCaptureSession() {
-        return this;
-    }
-
-    @Override
-    ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
-        final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
-        internalNotifyChildSessionStarted(mId, child.mId, clientContext);
-        return child;
-    }
-
-    /**
-     * Starts this session.
-     */
-    @Override
-    void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
-            @NonNull ComponentName component, int flags) {
-        runOnContentCaptureThread(
-                () -> startImpl(token, shareableActivityToken, component, flags));
-    }
-
-    private void startImpl(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
-               @NonNull ComponentName component, int flags) {
-        checkOnContentCaptureThread();
-        if (!isContentCaptureEnabled()) return;
-
-        if (sVerbose) {
-            Log.v(TAG, "start(): token=" + token + ", comp="
-                    + ComponentName.flattenToShortString(component));
-        }
-
-        if (hasStarted()) {
-            // TODO(b/122959591): make sure this is expected (and when), or use Log.w
-            if (sDebug) {
-                Log.d(TAG, "ignoring handleStartSession(" + token + "/"
-                        + ComponentName.flattenToShortString(component) + " while on state "
-                        + getStateAsString(mState));
-            }
-            return;
-        }
-        mState = STATE_WAITING_FOR_SERVER;
-        mApplicationToken = token;
-        mShareableActivityToken = shareableActivityToken;
-        mComponentName = component;
-
-        if (sVerbose) {
-            Log.v(TAG, "handleStartSession(): token=" + token + ", act="
-                    + getDebugState() + ", id=" + mId);
-        }
-
-        try {
-            mSystemServerInterface.startSession(mApplicationToken, mShareableActivityToken,
-                    component, mId, flags, mSessionStateReceiver);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error starting session for " + component.flattenToShortString() + ": " + e);
-        }
-    }
-    @Override
-    void onDestroy() {
-        clearAndRunOnContentCaptureThread(() -> {
-            try {
-                flush(FLUSH_REASON_SESSION_FINISHED);
-            } finally {
-                destroySession();
-            }
-        }, MSG_FLUSH);
-    }
-
-    /**
-     * Callback from {@code system_server} after call to {@link
-     * IContentCaptureManager#startSession(IBinder, ComponentName, String, int, IResultReceiver)}.
-     *
-     * @param resultCode session state
-     * @param binder handle to {@code IContentCaptureDirectManager}
-     * @hide
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public void onSessionStarted(int resultCode, @Nullable IBinder binder) {
-        checkOnContentCaptureThread();
-        if (binder != null) {
-            mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder);
-            mDirectServiceVulture = () -> {
-                Log.w(TAG, "Keeping session " + mId + " when service died");
-                mState = STATE_SERVICE_DIED;
-                mDisabled.set(true);
-            };
-            try {
-                binder.linkToDeath(mDirectServiceVulture, 0);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
-            }
-        }
-
-        if (isContentProtectionEnabled()) {
-            mContentProtectionEventProcessor =
-                    new ContentProtectionEventProcessor(
-                            mManager.getContentProtectionEventBuffer(),
-                            mContentCaptureHandler,
-                            mSystemServerInterface,
-                            mComponentName.getPackageName(),
-                            mManager.mOptions.contentProtectionOptions);
-        } else {
-            mContentProtectionEventProcessor = null;
-        }
-
-        if ((resultCode & STATE_DISABLED) != 0) {
-            resetSession(resultCode);
-        } else {
-            mState = resultCode;
-            mDisabled.set(false);
-            // Flush any pending data immediately as buffering forced until now.
-            flushIfNeeded(FLUSH_REASON_SESSION_CONNECTED);
-        }
-        if (sVerbose) {
-            Log.v(TAG, "handleSessionStarted() result: id=" + mId + " resultCode=" + resultCode
-                    + ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
-                    + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
-        }
-    }
-
-    /** @hide */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public void sendEvent(@NonNull ContentCaptureEvent event) {
-        sendEvent(event, /* forceFlush= */ false);
-    }
-
-    private void sendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
-        checkOnContentCaptureThread();
-        final int eventType = event.getType();
-        if (sVerbose) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
-        if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
-                && eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
-            // TODO(b/120494182): comment when this could happen (dialogs?)
-            if (sVerbose) {
-                Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
-                        + ContentCaptureEvent.getTypeAsString(eventType)
-                        + "): dropping because session not started yet");
-            }
-            return;
-        }
-        if (mDisabled.get()) {
-            // This happens when the event was queued in the handler before the sesison was ready,
-            // then handleSessionStarted() returned and set it as disabled - we need to drop it,
-            // otherwise it will keep triggering handleScheduleFlush()
-            if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled");
-            return;
-        }
-
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-            if (eventType == TYPE_VIEW_TREE_APPEARING) {
-                Trace.asyncTraceBegin(
-                        Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
-            }
-        }
-
-        if (isContentProtectionReceiverEnabled()) {
-            sendContentProtectionEvent(event);
-        }
-        if (isContentCaptureReceiverEnabled()) {
-            sendContentCaptureEvent(event, forceFlush);
-        }
-
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-            if (eventType == TYPE_VIEW_TREE_APPEARED) {
-                Trace.asyncTraceEnd(
-                        Trace.TRACE_TAG_VIEW, /* methodName= */ "sendEventAsync", /* cookie= */ 0);
-            }
-        }
-    }
-
-    private void sendContentProtectionEvent(@NonNull ContentCaptureEvent event) {
-        checkOnContentCaptureThread();
-        if (mContentProtectionEventProcessor != null) {
-            mContentProtectionEventProcessor.processEvent(event);
-        }
-    }
-
-    private void sendContentCaptureEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
-        checkOnContentCaptureThread();
-        final int eventType = event.getType();
-        final int maxBufferSize = mManager.mOptions.maxBufferSize;
-        if (mEvents == null) {
-            if (sVerbose) {
-                Log.v(TAG, "handleSendEvent(): creating buffer for " + maxBufferSize + " events");
-            }
-            mEvents = new ArrayList<>(maxBufferSize);
-        }
-
-        // Some type of events can be merged together
-        boolean addEvent = true;
-
-        if (eventType == TYPE_VIEW_TEXT_CHANGED) {
-            // We determine whether to add or merge the current event by following criteria:
-            // 1. Don't have composing span: always add.
-            // 2. Have composing span:
-            //    2.1 either last or current text is empty: add.
-            //    2.2 last event doesn't have composing span: add.
-            // Otherwise, merge.
-            final CharSequence text = event.getText();
-            final boolean hasComposingSpan = event.hasComposingSpan();
-            if (hasComposingSpan) {
-                ContentCaptureEvent lastEvent = null;
-                for (int index = mEvents.size() - 1; index >= 0; index--) {
-                    final ContentCaptureEvent tmpEvent = mEvents.get(index);
-                    if (event.getId().equals(tmpEvent.getId())) {
-                        lastEvent = tmpEvent;
-                        break;
-                    }
-                }
-                if (lastEvent != null && lastEvent.hasComposingSpan()) {
-                    final CharSequence lastText = lastEvent.getText();
-                    final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
-                            && !TextUtils.isEmpty(text);
-                    boolean equalContent =
-                            TextUtils.equals(lastText, text)
-                            && lastEvent.hasSameComposingSpan(event)
-                            && lastEvent.hasSameSelectionSpan(event);
-                    if (equalContent) {
-                        addEvent = false;
-                    } else if (bothNonEmpty) {
-                        lastEvent.mergeEvent(event);
-                        addEvent = false;
-                    }
-                    if (!addEvent && sVerbose) {
-                        Log.v(TAG, "Buffering VIEW_TEXT_CHANGED event, updated text="
-                                + getSanitizedString(text));
-                    }
-                }
-            }
-        }
-
-        if (!mEvents.isEmpty() && eventType == TYPE_VIEW_DISAPPEARED) {
-            final ContentCaptureEvent lastEvent = mEvents.get(mEvents.size() - 1);
-            if (lastEvent.getType() == TYPE_VIEW_DISAPPEARED
-                    && event.getSessionId() == lastEvent.getSessionId()) {
-                if (sVerbose) {
-                    Log.v(TAG, "Buffering TYPE_VIEW_DISAPPEARED events for session "
-                            + lastEvent.getSessionId());
-                }
-                lastEvent.mergeEvent(event);
-                addEvent = false;
-            }
-        }
-
-        if (addEvent) {
-            mEvents.add(event);
-        }
-
-        // TODO: we need to change when the flush happens so that we don't flush while the
-        //  composing span hasn't changed. But we might need to keep flushing the events for the
-        //  non-editable views and views that don't have the composing state; otherwise some other
-        //  Content Capture features may be delayed.
-
-        final int numberEvents = mEvents.size();
-
-        final boolean bufferEvent = numberEvents < maxBufferSize;
-
-        if (bufferEvent && !forceFlush) {
-            final int flushReason;
-            if (eventType == TYPE_VIEW_TEXT_CHANGED) {
-                mNextFlushForTextChanged = true;
-                flushReason = FLUSH_REASON_TEXT_CHANGE_TIMEOUT;
-            } else {
-                if (mNextFlushForTextChanged) {
-                    if (sVerbose) {
-                        Log.i(TAG, "Not scheduling flush because next flush is for text changed");
-                    }
-                    return;
-                }
-
-                flushReason = FLUSH_REASON_IDLE_TIMEOUT;
-            }
-            scheduleFlush(flushReason, /* checkExisting= */ true);
-            return;
-        }
-
-        if (mState != STATE_ACTIVE && numberEvents >= maxBufferSize) {
-            // Callback from startSession hasn't been called yet - typically happens on system
-            // apps that are started before the system service
-            // TODO(b/122959591): try to ignore session while system is not ready / boot
-            // not complete instead. Similarly, the manager service should return right away
-            // when the user does not have a service set
-            if (sDebug) {
-                Log.d(TAG, "Closing session for " + getDebugState()
-                        + " after " + numberEvents + " delayed events");
-            }
-            resetSession(STATE_DISABLED | STATE_NO_RESPONSE);
-            // TODO(b/111276913): denylist activity / use special flag to indicate that
-            // when it's launched again
-            return;
-        }
-        final int flushReason;
-        switch (eventType) {
-            case ContentCaptureEvent.TYPE_SESSION_STARTED:
-                flushReason = FLUSH_REASON_SESSION_STARTED;
-                break;
-            case ContentCaptureEvent.TYPE_SESSION_FINISHED:
-                flushReason = FLUSH_REASON_SESSION_FINISHED;
-                break;
-            case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING:
-                flushReason = FLUSH_REASON_VIEW_TREE_APPEARING;
-                break;
-            case ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED:
-                flushReason = FLUSH_REASON_VIEW_TREE_APPEARED;
-                break;
-            default:
-                flushReason = forceFlush ? FLUSH_REASON_FORCE_FLUSH : FLUSH_REASON_FULL;
-        }
-
-        flush(flushReason);
-    }
-
-    private boolean hasStarted() {
-        checkOnContentCaptureThread();
-        return mState != UNKNOWN_STATE;
-    }
-
-    private void scheduleFlush(@FlushReason int reason, boolean checkExisting) {
-        checkOnContentCaptureThread();
-        if (sVerbose) {
-            Log.v(TAG, "handleScheduleFlush(" + getDebugState(reason)
-                    + ", checkExisting=" + checkExisting);
-        }
-        if (!hasStarted()) {
-            if (sVerbose) Log.v(TAG, "handleScheduleFlush(): session not started yet");
-            return;
-        }
-
-        if (mDisabled.get()) {
-            // Should not be called on this state, as handleSendEvent checks.
-            // But we rather add one if check and log than re-schedule and keep the session alive...
-            Log.e(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): should not be called "
-                    + "when disabled. events=" + (mEvents == null ? null : mEvents.size()));
-            return;
-        }
-        if (checkExisting && mContentCaptureHandler.hasMessages(MSG_FLUSH)) {
-            // "Renew" the flush message by removing the previous one
-            mContentCaptureHandler.removeMessages(MSG_FLUSH);
-        }
-
-        final int flushFrequencyMs;
-        if (reason == FLUSH_REASON_TEXT_CHANGE_TIMEOUT) {
-            flushFrequencyMs = mManager.mOptions.textChangeFlushingFrequencyMs;
-        } else {
-            if (reason != FLUSH_REASON_IDLE_TIMEOUT) {
-                if (sDebug) {
-                    Log.d(TAG, "handleScheduleFlush(" + getDebugState(reason) + "): not a timeout "
-                            + "reason because mDirectServiceInterface is not ready yet");
-                }
-            }
-            flushFrequencyMs = mManager.mOptions.idleFlushingFrequencyMs;
-        }
-
-        mNextFlush = System.currentTimeMillis() + flushFrequencyMs;
-        if (sVerbose) {
-            Log.v(TAG, "handleScheduleFlush(): scheduled to flush in "
-                    + flushFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
-        }
-        // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
-        mContentCaptureHandler.postDelayed(() ->
-                flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
-    }
-
-    private void flushIfNeeded(@FlushReason int reason) {
-        checkOnContentCaptureThread();
-        if (mEvents == null || mEvents.isEmpty()) {
-            if (sVerbose) Log.v(TAG, "Nothing to flush");
-            return;
-        }
-        flush(reason);
-    }
-
-    /** @hide */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    @Override
-    public void flush(@FlushReason int reason) {
-        runOnContentCaptureThread(() -> flushImpl(reason));
-    }
-
-    private void flushImpl(@FlushReason int reason) {
-        checkOnContentCaptureThread();
-        if (mEvents == null || mEvents.size() == 0) {
-            if (sVerbose) {
-                Log.v(TAG, "Don't flush for empty event buffer.");
-            }
-            return;
-        }
-
-        if (mDisabled.get()) {
-            Log.e(TAG, "handleForceFlush(" + getDebugState(reason) + "): should not be when "
-                    + "disabled");
-            return;
-        }
-
-        if (!isContentCaptureReceiverEnabled()) {
-            return;
-        }
-
-        if (mDirectServiceInterface == null) {
-            if (sVerbose) {
-                Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
-                        + "client not ready: " + mEvents);
-            }
-            if (!mContentCaptureHandler.hasMessages(MSG_FLUSH)) {
-                scheduleFlush(reason, /* checkExisting= */ false);
-            }
-            return;
-        }
-
-        mNextFlushForTextChanged = false;
-
-        final int numberEvents = mEvents.size();
-        final String reasonString = getFlushReasonAsString(reason);
-
-        if (sVerbose) {
-            ContentCaptureEvent event = mEvents.get(numberEvents - 1);
-            String forceString = (reason == FLUSH_REASON_FORCE_FLUSH) ? ". The force flush event "
-                    + ContentCaptureEvent.getTypeAsString(event.getType()) : "";
-            Log.v(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason)
-                    + forceString);
-        }
-        if (mFlushHistory != null) {
-            // Logs reason, size, max size, idle timeout
-            final String logRecord = "r=" + reasonString + " s=" + numberEvents
-                    + " m=" + mManager.mOptions.maxBufferSize
-                    + " i=" + mManager.mOptions.idleFlushingFrequencyMs;
-            mFlushHistory.log(logRecord);
-        }
-        try {
-            mContentCaptureHandler.removeMessages(MSG_FLUSH);
-
-            final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
-            mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error sending " + numberEvents + " for " + getDebugState()
-                    + ": " + e);
-        }
-    }
-
-    @Override
-    public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
-        internalNotifyContextUpdated(mId, context);
-    }
-
-    /**
-     * Resets the buffer and return a {@link ParceledListSlice} with the previous events.
-     */
-    @NonNull
-    private ParceledListSlice<ContentCaptureEvent> clearEvents() {
-        checkOnContentCaptureThread();
-        // NOTE: we must save a reference to the current mEvents and then set it to to null,
-        // otherwise clearing it would clear it in the receiving side if the service is also local.
-        if (mEvents == null) {
-            return new ParceledListSlice<>(Collections.EMPTY_LIST);
-        }
-
-        final List<ContentCaptureEvent> events = new ArrayList<>(mEvents);
-        mEvents.clear();
-        return new ParceledListSlice<>(events);
-    }
-
-    /** hide */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public void destroySession() {
-        checkOnContentCaptureThread();
-        if (sDebug) {
-            Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with "
-                    + (mEvents == null ? 0 : mEvents.size()) + " event(s) for "
-                    + getDebugState());
-        }
-
-        reportWrongThreadMetric();
-        try {
-            mSystemServerInterface.finishSession(mId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error destroying system-service session " + mId + " for "
-                    + getDebugState() + ": " + e);
-        }
-
-        if (mDirectServiceInterface != null) {
-            mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
-        }
-        mDirectServiceInterface = null;
-        mContentProtectionEventProcessor = null;
-        mEventProcessQueue.clear();
-    }
-
-    // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
-    // clearings out.
-    /** @hide */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    public void resetSession(int newState) {
-        checkOnContentCaptureThread();
-        if (sVerbose) {
-            Log.v(TAG, "handleResetSession(" + getActivityName() + "): from "
-                    + getStateAsString(mState) + " to " + getStateAsString(newState));
-        }
-        mState = newState;
-        mDisabled.set((newState & STATE_DISABLED) != 0);
-        // TODO(b/122454205): must reset children (which currently is owned by superclass)
-        mApplicationToken = null;
-        mShareableActivityToken = null;
-        mComponentName = null;
-        mEvents = null;
-        if (mDirectServiceInterface != null) {
-            try {
-                mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0);
-            } catch (NoSuchElementException e) {
-                Log.w(TAG, "IContentCaptureDirectManager does not exist");
-            }
-        }
-        mDirectServiceInterface = null;
-        mContentProtectionEventProcessor = null;
-        mContentCaptureHandler.removeMessages(MSG_FLUSH);
-    }
-
-    @Override
-    void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
-        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
-                .setViewNode(node.mNode);
-        enqueueEvent(event);
-    }
-
-    @Override
-    void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
-        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED)
-                .setAutofillId(id);
-        enqueueEvent(event);
-    }
-
-    @Override
-    void internalNotifyViewTextChanged(
-            int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
-        // Since the same CharSequence instance may be reused in the TextView, we need to make
-        // a copy of its content so that its value will not be changed by subsequent updates
-        // in the TextView.
-        CharSequence trimmed = TextUtils.trimToParcelableSize(text);
-        final CharSequence eventText = trimmed != null && trimmed == text
-                ? trimmed.toString()
-                : trimmed;
-
-        final int composingStart;
-        final int composingEnd;
-        if (text instanceof Spannable) {
-            composingStart = BaseInputConnection.getComposingSpanStart((Spannable) text);
-            composingEnd = BaseInputConnection.getComposingSpanEnd((Spannable) text);
-        } else {
-            composingStart = ContentCaptureEvent.MAX_INVALID_VALUE;
-            composingEnd = ContentCaptureEvent.MAX_INVALID_VALUE;
-        }
-
-        final int startIndex = Selection.getSelectionStart(text);
-        final int endIndex = Selection.getSelectionEnd(text);
-
-        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED)
-                .setAutofillId(id).setText(eventText)
-                .setComposingIndex(composingStart, composingEnd)
-                .setSelectionIndex(startIndex, endIndex);
-        enqueueEvent(event);
-    }
-
-    @Override
-    void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
-        final ContentCaptureEvent event =
-                new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
-                        .setInsets(viewInsets);
-        enqueueEvent(event);
-    }
-
-    @Override
-    public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
-        final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
-        final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled();
-        final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH;
-
-        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type);
-        enqueueEvent(event, forceFlush);
-    }
-
-    @Override
-    public void internalNotifySessionResumed() {
-        final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_RESUMED);
-        enqueueEvent(event, FORCE_FLUSH);
-    }
-
-    @Override
-    public void internalNotifySessionPaused() {
-        final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_PAUSED);
-        enqueueEvent(event, FORCE_FLUSH);
-    }
-
-    @Override
-    boolean isContentCaptureEnabled() {
-        return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
-    }
-
-    // Called by ContentCaptureManager.isContentCaptureEnabled
-    boolean isDisabled() {
-        return mDisabled.get();
-    }
-
-    /**
-     * Sets the disabled state of content capture.
-     *
-     * @return whether disabled state was changed.
-     */
-    boolean setDisabled(boolean disabled) {
-        return mDisabled.compareAndSet(!disabled, disabled);
-    }
-
-    @Override
-    void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
-            @NonNull ContentCaptureContext clientContext) {
-        final ContentCaptureEvent event =
-                new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
-                        .setParentSessionId(parentSessionId)
-                        .setClientContext(clientContext);
-        enqueueEvent(event, FORCE_FLUSH);
-    }
-
-    @Override
-    void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
-        final ContentCaptureEvent event =
-                new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
-                        .setParentSessionId(parentSessionId);
-        enqueueEvent(event, FORCE_FLUSH);
-    }
-
-    @Override
-    void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
-        final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
-                .setClientContext(context);
-        enqueueEvent(event, FORCE_FLUSH);
-    }
-
-    @Override
-    public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
-        final ContentCaptureEvent event =
-                new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
-                        .setBounds(bounds);
-        enqueueEvent(event);
-    }
-
-    private List<ContentCaptureEvent> clearBufferEvents() {
-        final ArrayList<ContentCaptureEvent> bufferEvents = new ArrayList<>();
-        ContentCaptureEvent event;
-        while ((event = mEventProcessQueue.poll()) != null) {
-            bufferEvents.add(event);
-        }
-        return bufferEvents;
-    }
-
-    private void enqueueEvent(@NonNull final ContentCaptureEvent event) {
-        enqueueEvent(event, /* forceFlush */ false);
-    }
-
-    /**
-     * Enqueue the event into {@code mEventProcessBuffer} if it is not an urgent request. Otherwise,
-     * clear the buffer events then starting sending out current event.
-     */
-    private void enqueueEvent(@NonNull final ContentCaptureEvent event, boolean forceFlush) {
-        if (forceFlush || mEventProcessQueue.size() >= mManager.mOptions.maxBufferSize - 1) {
-            // The buffer events are cleared in the same thread first to prevent new events
-            // being added during the time of context switch. This would disrupt the sequence
-            // of events.
-            final List<ContentCaptureEvent> batchEvents = clearBufferEvents();
-            runOnContentCaptureThread(() -> {
-                for (int i = 0; i < batchEvents.size(); i++) {
-                    sendEvent(batchEvents.get(i));
-                }
-                sendEvent(event, /* forceFlush= */ true);
-            });
-        } else {
-            mEventProcessQueue.offer(event);
-        }
-    }
-
-    @Override
-    public void notifyContentCaptureEvents(
-            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
-        runOnUiThread(() -> {
-            prepareViewStructures(contentCaptureEvents);
-            runOnContentCaptureThread(() ->
-                    notifyContentCaptureEventsImpl(contentCaptureEvents));
-        });
-    }
-
-    /**
-     * Traverse events and pre-process {@link View} events to {@link ViewStructureSession} events.
-     * If a {@link View} event is invalid, an empty {@link ViewStructureSession} will still be
-     * provided.
-     */
-    private void prepareViewStructures(
-            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
-        for (int i = 0; i < contentCaptureEvents.size(); i++) {
-            int sessionId = contentCaptureEvents.keyAt(i);
-            ArrayList<Object> events = contentCaptureEvents.valueAt(i);
-            for_each_event: for (int j = 0; j < events.size(); j++) {
-                Object event = events.get(j);
-                if (event instanceof View) {
-                    View view = (View) event;
-                    ContentCaptureSession session = view.getContentCaptureSession();
-                    ViewStructureSession structureSession = new ViewStructureSession();
-
-                    // Replace the View event with ViewStructureSession no matter the data is
-                    // available or not. This is to ensure the sequence of the events are still
-                    // the same. Calls to notifyViewAppeared will check the availability later.
-                    events.set(j, structureSession);
-                    if (session == null) {
-                        Log.w(TAG, "no content capture session on view: " + view);
-                        continue for_each_event;
-                    }
-                    int actualId = session.getId();
-                    if (actualId != sessionId) {
-                        Log.w(TAG, "content capture session mismatch for view (" + view
-                                + "): was " + sessionId + " before, it's " + actualId + " now");
-                        continue for_each_event;
-                    }
-                    ViewStructure structure = session.newViewStructure(view);
-                    view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
-
-                    structureSession.setSession(session);
-                    structureSession.setStructure(structure);
-                }
-            }
-        }
-    }
-
-    private void notifyContentCaptureEventsImpl(
-            @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
-        checkOnContentCaptureThread();
-        try {
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
-                Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents");
-            }
-            for (int i = 0; i < contentCaptureEvents.size(); i++) {
-                int sessionId = contentCaptureEvents.keyAt(i);
-                internalNotifyViewTreeEvent(sessionId, /* started= */ true);
-                ArrayList<Object> events = contentCaptureEvents.valueAt(i);
-                for_each_event: for (int j = 0; j < events.size(); j++) {
-                    Object event = events.get(j);
-                    if (event instanceof AutofillId) {
-                        internalNotifyViewDisappeared(sessionId, (AutofillId) event);
-                    } else if (event instanceof ViewStructureSession viewStructureSession) {
-                        viewStructureSession.notifyViewAppeared();
-                    } else if (event instanceof Insets) {
-                        internalNotifyViewInsetsChanged(sessionId, (Insets) event);
-                    } else {
-                        Log.w(TAG, "invalid content capture event: " + event);
-                    }
-                }
-                internalNotifyViewTreeEvent(sessionId, /* started= */ false);
-            }
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
-    }
-
-    @Override
-    void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
-        super.dump(prefix, pw);
-
-        pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
-        pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId());
-        if (mDirectServiceInterface != null) {
-            pw.print(prefix); pw.print("mDirectServiceInterface: ");
-            pw.println(mDirectServiceInterface);
-        }
-        pw.print(prefix); pw.print("mDisabled: "); pw.println(mDisabled.get());
-        pw.print(prefix); pw.print("isEnabled(): "); pw.println(isContentCaptureEnabled());
-        pw.print(prefix); pw.print("state: "); pw.println(getStateAsString(mState));
-        if (mApplicationToken != null) {
-            pw.print(prefix); pw.print("app token: "); pw.println(mApplicationToken);
-        }
-        if (mShareableActivityToken != null) {
-            pw.print(prefix); pw.print("sharable activity token: ");
-            pw.println(mShareableActivityToken);
-        }
-        if (mComponentName != null) {
-            pw.print(prefix); pw.print("component name: ");
-            pw.println(mComponentName.flattenToShortString());
-        }
-        if (mEvents != null && !mEvents.isEmpty()) {
-            final int numberEvents = mEvents.size();
-            pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents);
-            pw.print('/'); pw.println(mManager.mOptions.maxBufferSize);
-            if (sVerbose && numberEvents > 0) {
-                final String prefix3 = prefix + "  ";
-                for (int i = 0; i < numberEvents; i++) {
-                    final ContentCaptureEvent event = mEvents.get(i);
-                    pw.print(prefix3); pw.print(i); pw.print(": "); event.dump(pw);
-                    pw.println();
-                }
-            }
-            pw.print(prefix); pw.print("mNextFlushForTextChanged: ");
-            pw.println(mNextFlushForTextChanged);
-            pw.print(prefix); pw.print("flush frequency: ");
-            if (mNextFlushForTextChanged) {
-                pw.println(mManager.mOptions.textChangeFlushingFrequencyMs);
-            } else {
-                pw.println(mManager.mOptions.idleFlushingFrequencyMs);
-            }
-            pw.print(prefix); pw.print("next flush: ");
-            TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw);
-            pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")");
-        }
-        if (mFlushHistory != null) {
-            pw.print(prefix); pw.println("flush history:");
-            mFlushHistory.reverseDump(/* fd= */ null, pw, /* args= */ null); pw.println();
-        } else {
-            pw.print(prefix); pw.println("not logging flush history");
-        }
-
-        super.dump(prefix, pw);
-    }
-
-    /**
-     * Gets a string that can be used to identify the activity on logging statements.
-     */
-    private String getActivityName() {
-        return mComponentName == null
-                ? "pkg:" + mContext.getPackageName()
-                : "act:" + mComponentName.flattenToShortString();
-    }
-
-    @NonNull
-    private String getDebugState() {
-        return getActivityName() + " [state=" + getStateAsString(mState) + ", disabled="
-                + mDisabled.get() + "]";
-    }
-
-    @NonNull
-    private String getDebugState(@FlushReason int reason) {
-        return getDebugState() + ", reason=" + getFlushReasonAsString(reason);
-    }
-
-    private boolean isContentProtectionReceiverEnabled() {
-        return mManager.mOptions.contentProtectionOptions.enableReceiver;
-    }
-
-    private boolean isContentCaptureReceiverEnabled() {
-        return mManager.mOptions.enableReceiver;
-    }
-
-    private boolean isContentProtectionEnabled() {
-        // Should not be possible for mComponentName to be null here but check anyway
-        // Should not be possible for groups to be empty if receiver is enabled but check anyway
-        return mManager.mOptions.contentProtectionOptions.enableReceiver
-                && mManager.getContentProtectionEventBuffer() != null
-                && mComponentName != null
-                && (!mManager.mOptions.contentProtectionOptions.requiredGroups.isEmpty()
-                        || !mManager.mOptions.contentProtectionOptions.optionalGroups.isEmpty());
-    }
-
-    /**
-     * Checks that the current work is running on the assigned thread from {@code mHandler} and
-     * count the number of times running on the wrong thread.
-     *
-     * <p>It is not guaranteed that the callers always invoke function from a single thread.
-     * Therefore, accessing internal properties in {@link MainContentCaptureSession} should
-     * always delegate to the assigned thread from {@code mHandler} for synchronization.</p>
-     */
-    private void checkOnContentCaptureThread() {
-        final boolean onContentCaptureThread = mContentCaptureHandler.getLooper().isCurrentThread();
-        if (!onContentCaptureThread) {
-            mWrongThreadCount.incrementAndGet();
-            Log.e(TAG, "MainContentCaptureSession running on " + Thread.currentThread());
-        }
-    }
-
-    /** Reports number of times running on the wrong thread. */
-    private void reportWrongThreadMetric() {
-        Counter.logIncrement(
-                CONTENT_CAPTURE_WRONG_THREAD_METRIC_ID, mWrongThreadCount.getAndSet(0));
-    }
-
-    /**
-     * Ensures that {@code r} will be running on the assigned thread.
-     *
-     * <p>This is to prevent unnecessary delegation to Handler that results in fragmented runnable.
-     * </p>
-     */
-    private void runOnContentCaptureThread(@NonNull Runnable r) {
-        if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
-            mContentCaptureHandler.post(r);
-        } else {
-            r.run();
-        }
-    }
-
-    private void clearAndRunOnContentCaptureThread(@NonNull Runnable r, int what) {
-        if (!mContentCaptureHandler.getLooper().isCurrentThread()) {
-            mContentCaptureHandler.removeMessages(what);
-            mContentCaptureHandler.post(r);
-        } else {
-            r.run();
-        }
-    }
-
-    private void runOnUiThread(@NonNull Runnable r) {
-        if (mUiHandler.getLooper().isCurrentThread()) {
-            r.run();
-        } else {
-            mUiHandler.post(r);
-        }
-    }
-
-    /**
-     * Holds {@link ContentCaptureSession} and related {@link ViewStructure} for processing.
-     */
-    private static final class ViewStructureSession {
-        @Nullable private ContentCaptureSession mSession;
-        @Nullable private ViewStructure mStructure;
-
-        ViewStructureSession() {}
-
-        void setSession(@Nullable ContentCaptureSession session) {
-            this.mSession = session;
-        }
-
-        void setStructure(@Nullable ViewStructure struct) {
-            this.mStructure = struct;
-        }
-
-        /**
-         * Calls {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)} if the session and
-         * the view structure are available.
-         */
-        void notifyViewAppeared() {
-            if (mSession != null && mStructure != null) {
-                mSession.notifyViewAppeared(mStructure);
-            }
-        }
-    }
-}
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index c0d31fa..4d4e4af 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -28,6 +28,14 @@
 }
 
 flag {
+    name: "enable_vector_cursor_a11y_settings"
+    namespace: "systemui"
+    description: "Feature flag to enable accessibility settings for vector cursors."
+    bug: "302275042"
+    is_fixed_read_only: true
+}
+
+flag {
   name: "sensitive_content_app_protection_api"
   is_exported: true
   namespace: "permissions"
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index acc74b2..e7a2fb9 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -29,9 +29,11 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.util.ExceptionUtils;
 import android.view.WindowManager;
 import android.window.ImeOnBackInvokedDispatcher;
 
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.inputmethod.DirectBootAwareness;
 import com.android.internal.inputmethod.IBooleanListener;
 import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
@@ -48,6 +50,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
 /**
@@ -62,6 +65,10 @@
  * a wrapper method in {@link InputMethodManagerGlobal} instead of making this class public.</p>
  */
 final class IInputMethodManagerGlobalInvoker {
+
+    /** The threshold in milliseconds for an {@link AndroidFuture} completion signal. */
+    private static final long TIMEOUT_MS = 10_000;
+
     @Nullable
     private static volatile IInputMethodManager sServiceCache = null;
 
@@ -801,9 +808,13 @@
             return;
         }
         try {
-            service.finishTrackingPendingImeVisibilityRequests();
+            final var completionSignal = new AndroidFuture<Void>();
+            service.finishTrackingPendingImeVisibilityRequests(completionSignal);
+            completionSignal.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        } catch (Exception e) {
+            throw ExceptionUtils.propagate(e);
         }
     }
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 2d4bb90..0c63e58 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -129,6 +129,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Proxy;
 import java.util.Arrays;
 import java.util.Collections;
@@ -2630,13 +2631,17 @@
                 return false;
             }
             if (useDelegation) {
+                WeakReference<Executor> executorRef = new WeakReference<>(executor);
+                WeakReference<Consumer<Boolean>> callbackRef = new WeakReference<>(callback);
                 if (useCallback) {
                     IBooleanListener listener = new IBooleanListener.Stub() {
                         @Override
                         public void onResult(boolean value) {
-                            executor.execute(() -> {
-                                callback.accept(value);
-                            });
+                            Executor executor = executorRef.get();
+                            Consumer<Boolean> callback = callbackRef.get();
+                            if (executor != null && callback != null) {
+                                executor.execute(() -> callback.accept(value));
+                            }
                         }
                     };
                     if (!IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegationAsync(
diff --git a/core/java/android/view/textclassifier/intent/OWNERS b/core/java/android/view/textclassifier/intent/OWNERS
index ac80d9f..3465fe6 100644
--- a/core/java/android/view/textclassifier/intent/OWNERS
+++ b/core/java/android/view/textclassifier/intent/OWNERS
@@ -4,5 +4,4 @@
 toki@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
-augale@google.com
 joannechung@google.com
diff --git a/core/java/android/widget/CursorTreeAdapter.java b/core/java/android/widget/CursorTreeAdapter.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 4099e88..3c5623f 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1044,11 +1044,6 @@
         public int getActionTag() {
             return SET_PENDING_INTENT_TEMPLATE_TAG;
         }
-
-        @Override
-        public void visitUris(@NonNull Consumer<Uri> visitor) {
-            mPendingIntentTemplate.visitUris(visitor);
-        }
     }
 
     /**
@@ -1424,6 +1419,10 @@
                                 context.unbindService(this);
                             }
 
+                            if (items == null) {
+                                items = new RemoteCollectionItems.Builder().build();
+                            }
+
                             result.complete(items);
                         }
 
@@ -1524,11 +1523,6 @@
         public int getActionTag() {
             return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
         }
-
-        @Override
-        public void visitUris(@NonNull Consumer<Uri> visitor) {
-            mIntent.visitUris(visitor);
-        }
     }
 
     /**
@@ -1607,11 +1601,6 @@
         public int getActionTag() {
             return SET_ON_CLICK_RESPONSE_TAG;
         }
-
-        @Override
-        public void visitUris(@NonNull Consumer<Uri> visitor) {
-            mResponse.visitUris(visitor);
-        }
     }
 
     /** Helper action to configure handwriting delegation via {@link PendingIntent}. */
@@ -1659,11 +1648,6 @@
         public int getActionTag() {
             return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG;
         }
-
-        @Override
-        public void visitUris(@NonNull Consumer<Uri> visitor) {
-            mPendingIntent.visitUris(visitor);
-        }
     }
 
     /**
@@ -1734,11 +1718,6 @@
         public int getActionTag() {
             return SET_ON_CHECKED_CHANGE_RESPONSE_TAG;
         }
-
-        @Override
-        public void visitUris(@NonNull Consumer<Uri> visitor) {
-            mResponse.visitUris(visitor);
-        }
     }
 
     /** @hide **/
@@ -2298,10 +2277,6 @@
                     final Icon icon = (Icon) getParameterValue(null);
                     if (icon != null) visitIconUri(icon, visitor);
                     break;
-                case INTENT:
-                    final Intent intent = (Intent) getParameterValue(null);
-                    if (intent != null) intent.visitUris(visitor);
-                    break;
                 // TODO(b/281044385): Should we do anything about type BUNDLE?
             }
         }
@@ -7222,20 +7197,6 @@
             mElementNames = parcel.createStringArrayList();
         }
 
-        /**
-         * See {@link RemoteViews#visitUris(Consumer)}.
-         *
-         * @hide
-         */
-        public void visitUris(@NonNull Consumer<Uri> visitor) {
-            if (mPendingIntent != null) {
-                mPendingIntent.visitUris(visitor);
-            }
-            if (mFillIntent != null) {
-                mFillIntent.visitUris(visitor);
-            }
-        }
-
         private void handleViewInteraction(
                 View v,
                 InteractionHandler handler) {
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
old mode 100755
new mode 100644
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 78dd3b1..f7e0ec8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4817,7 +4817,11 @@
         if (mFontWeightAdjustment != 0
                 && mFontWeightAdjustment != Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED) {
             if (tf == null) {
-                tf = Typeface.DEFAULT;
+                if (Flags.fixNullTypefaceBolding()) {
+                    tf = Typeface.DEFAULT_BOLD;
+                } else {
+                    tf = Typeface.DEFAULT;
+                }
             } else {
                 int newWeight = Math.min(
                         Math.max(tf.getWeight() + mFontWeightAdjustment, FontStyle.FONT_WEIGHT_MIN),
@@ -14047,6 +14051,9 @@
 
     @Override
     public void autofill(AutofillValue value) {
+        if (android.view.autofill.Helper.sVerbose) {
+            Log.v(LOG_TAG, "autofill() called on textview for id:" + getAutofillId());
+        }
         if (!isTextAutofillable()) {
             Log.w(LOG_TAG, "cannot autofill non-editable TextView: " + this);
             return;
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
index 503e542..56a2cf7 100644
--- a/core/java/android/widget/flags/notification_widget_flags.aconfig
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -57,3 +57,13 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "conversation_layout_use_maximum_child_height"
+  namespace: "systemui"
+  description: "MessagingChild always needs to be measured during MessagingLinearLayout onMeasure."
+  bug: "324537506"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
\ No newline at end of file
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index f24bc74..59639d0 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -113,6 +114,10 @@
     private final CustomAnimationInfo mCustomAnimationInfo;
 
     private final int mLetterboxColor;
+    @NonNull
+    private final Rect mTouchableRegion;
+
+    private final boolean mAppProgressGenerationAllowed;
 
     /**
      * Create a new {@link BackNavigationInfo} instance.
@@ -128,7 +133,9 @@
             boolean isPrepareRemoteAnimation,
             boolean isAnimationCallback,
             @Nullable CustomAnimationInfo customAnimationInfo,
-            int letterboxColor) {
+            int letterboxColor,
+            @Nullable Rect touchableRegion,
+            boolean appProgressGenerationAllowed) {
         mType = type;
         mOnBackNavigationDone = onBackNavigationDone;
         mOnBackInvokedCallback = onBackInvokedCallback;
@@ -136,6 +143,8 @@
         mAnimationCallback = isAnimationCallback;
         mCustomAnimationInfo = customAnimationInfo;
         mLetterboxColor = letterboxColor;
+        mTouchableRegion = new Rect(touchableRegion);
+        mAppProgressGenerationAllowed = appProgressGenerationAllowed;
     }
 
     private BackNavigationInfo(@NonNull Parcel in) {
@@ -146,6 +155,8 @@
         mAnimationCallback = in.readBoolean();
         mCustomAnimationInfo = in.readTypedObject(CustomAnimationInfo.CREATOR);
         mLetterboxColor = in.readInt();
+        mTouchableRegion = in.readTypedObject(Rect.CREATOR);
+        mAppProgressGenerationAllowed = in.readBoolean();
     }
 
     /** @hide */
@@ -158,6 +169,8 @@
         dest.writeBoolean(mAnimationCallback);
         dest.writeTypedObject(mCustomAnimationInfo, flags);
         dest.writeInt(mLetterboxColor);
+        dest.writeTypedObject(mTouchableRegion, flags);
+        dest.writeBoolean(mAppProgressGenerationAllowed);
     }
 
     /**
@@ -206,6 +219,24 @@
     public int getLetterboxColor() {
         return mLetterboxColor;
     }
+
+    /**
+     * @return The app window region where the client can handle touch event.
+     * @hide
+     */
+    @NonNull
+    public Rect getTouchableRegion() {
+        return mTouchableRegion;
+    }
+
+    /**
+     * @return The client side view is able to intercept back progress event.
+     * @hide
+     */
+    public boolean isAppProgressGenerationAllowed() {
+        return mAppProgressGenerationAllowed;
+    }
+
     /**
      * Callback to be called when the back preview is finished in order to notify the server that
      * it can clean up the resources created for the animation.
@@ -402,6 +433,8 @@
         private boolean mAnimationCallback = false;
 
         private int mLetterboxColor = Color.TRANSPARENT;
+        private Rect mTouchableRegion;
+        private boolean mAppProgressGenerationAllowed;
 
         /**
          * @see BackNavigationInfo#getType()
@@ -478,6 +511,22 @@
         }
 
         /**
+         * @param rect Non-empty for frame of current focus window.
+         */
+        public Builder setTouchableRegion(Rect rect) {
+            mTouchableRegion = new Rect(rect);
+            return this;
+        }
+
+        /**
+         * @param allowed Whether client side view able to intercept back progress event.
+         */
+        public Builder setAppProgressAllowed(boolean allowed) {
+            mAppProgressGenerationAllowed = allowed;
+            return this;
+        }
+
+        /**
          * Builds and returns an instance of {@link BackNavigationInfo}
          */
         public BackNavigationInfo build() {
@@ -486,7 +535,9 @@
                     mPrepareRemoteAnimation,
                     mAnimationCallback,
                     mCustomAnimationInfo,
-                    mLetterboxColor);
+                    mLetterboxColor,
+                    mTouchableRegion,
+                    mAppProgressGenerationAllowed);
         }
     }
 }
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index 94d7811..d28500c 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -19,8 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.FloatProperty;
+import android.util.TimeUtils;
+import android.view.Choreographer;
 
 import com.android.internal.dynamicanimation.animation.DynamicAnimation;
+import com.android.internal.dynamicanimation.animation.FlingAnimation;
+import com.android.internal.dynamicanimation.animation.FloatValueHolder;
 import com.android.internal.dynamicanimation.animation.SpringAnimation;
 import com.android.internal.dynamicanimation.animation.SpringForce;
 
@@ -40,6 +44,7 @@
      *  always receive progress values in [0, 1].
      */
     private static final float SCALE_FACTOR = 100f;
+    private static final float FLING_FRICTION = 8f;
     private final SpringAnimation mSpring;
     private ProgressCallback mCallback;
     private float mProgress = 0;
@@ -48,11 +53,17 @@
     private boolean mBackAnimationInProgress = false;
     @Nullable
     private Runnable mBackCancelledFinishRunnable;
+    @Nullable
+    private Runnable mBackInvokedFinishRunnable;
+    private FlingAnimation mBackInvokedFlingAnim;
     private final DynamicAnimation.OnAnimationEndListener mOnAnimationEndListener =
             (animation, canceled, value, velocity) -> {
-                invokeBackCancelledRunnable();
+                if (mBackCancelledFinishRunnable != null) invokeBackCancelledRunnable();
+                if (mBackInvokedFinishRunnable != null) invokeBackInvokedRunnable();
                 reset();
             };
+    private final DynamicAnimation.OnAnimationUpdateListener mOnBackInvokedFlingUpdateListener =
+            (animation, progress, velocity) -> updateProgressValue(progress, velocity);
 
 
     private void setProgress(float progress) {
@@ -78,7 +89,7 @@
 
     @Override
     public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
-        updateProgressValue(value, velocity);
+        if (mBackInvokedFinishRunnable == null) updateProgressValue(value, velocity);
     }
 
 
@@ -134,6 +145,12 @@
             // Ensure that last progress value that apps see is 0
             updateProgressValue(0, 0);
             invokeBackCancelledRunnable();
+        } else if (mBackInvokedFinishRunnable != null) {
+            invokeBackInvokedRunnable();
+        }
+        if (mBackInvokedFlingAnim != null) {
+            mBackInvokedFlingAnim.cancel();
+            mBackInvokedFlingAnim = null;
         }
         mSpring.animateToFinalPosition(0);
         if (mSpring.canSkipToEnd()) {
@@ -149,6 +166,30 @@
     }
 
     /**
+     * Animate the back progress animation a bit further with a high friction considering the
+     * current progress and velocity.
+     *
+     * @param finishCallback the callback to be invoked when the final destination is reached
+     */
+    public void onBackInvoked(@NonNull Runnable finishCallback) {
+        mBackInvokedFinishRunnable = finishCallback;
+        mSpring.animateToFinalPosition(0);
+
+        mBackInvokedFlingAnim = new FlingAnimation(new FloatValueHolder())
+                .setStartValue(mProgress)
+                .setFriction(FLING_FRICTION)
+                .setStartVelocity(mVelocity)
+                .setMinValue(0)
+                .setMaxValue(SCALE_FACTOR);
+        mBackInvokedFlingAnim.addUpdateListener(mOnBackInvokedFlingUpdateListener);
+        mBackInvokedFlingAnim.addEndListener(mOnAnimationEndListener);
+        mBackInvokedFlingAnim.start();
+        // do an animation-frame immediately to prevent idle frame
+        mBackInvokedFlingAnim.doAnimationFrame(
+                Choreographer.getInstance().getLastFrameTimeNanos() / TimeUtils.NANOS_PER_MS);
+    }
+
+    /**
      * Animate the back progress animation from current progress to start position.
      * This should be called when back is cancelled.
      *
@@ -196,4 +237,11 @@
         mBackCancelledFinishRunnable = null;
     }
 
+    private void invokeBackInvokedRunnable() {
+        mBackInvokedFlingAnim.removeUpdateListener(mOnBackInvokedFlingUpdateListener);
+        mBackInvokedFlingAnim.removeEndListener(mOnAnimationEndListener);
+        mBackInvokedFinishRunnable.run();
+        mBackInvokedFinishRunnable = null;
+    }
+
 }
\ No newline at end of file
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index 2a12507..ce1f986 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -55,6 +55,9 @@
     static final int RESULT_CODE_UNREGISTER = 1;
     @NonNull
     private final ResultReceiver mResultReceiver;
+    // The handler to run callbacks on. This should be on the same thread
+    // the ViewRootImpl holding IME's WindowOnBackInvokedDispatcher is created on.
+    private Handler mHandler;
 
     public ImeOnBackInvokedDispatcher(Handler handler) {
         mResultReceiver = new ResultReceiver(handler) {
@@ -68,6 +71,10 @@
         };
     }
 
+    void setHandler(@NonNull Handler handler) {
+        mHandler = handler;
+    }
+
     /**
      * Override this method to return the {@link WindowOnBackInvokedDispatcher} of the window
      * that should receive the forwarded callback.
@@ -326,7 +333,7 @@
 
         @Override
         public void onBackInvoked() {
-            mCallback.onBackInvoked();
+            mHandler.post(mCallback::onBackInvoked);
         }
 
         @Override
@@ -336,7 +343,7 @@
 
         private void maybeRunOnAnimationCallback(Consumer<OnBackAnimationCallback> block) {
             if (mCallback instanceof OnBackAnimationCallback) {
-                block.accept((OnBackAnimationCallback) mCallback);
+                mHandler.post(() -> block.accept((OnBackAnimationCallback) mCallback));
             }
         }
     }
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 1d42c93..8ded608 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -663,6 +663,7 @@
         private final Rect mStartAbsBounds = new Rect();
         private final Rect mEndAbsBounds = new Rect();
         private final Point mEndRelOffset = new Point();
+        private final Point mEndParentSize = new Point();
         private ActivityManager.RunningTaskInfo mTaskInfo = null;
         private boolean mAllowEnterPip;
         private int mStartDisplayId = INVALID_DISPLAY;
@@ -697,6 +698,7 @@
             mStartAbsBounds.readFromParcel(in);
             mEndAbsBounds.readFromParcel(in);
             mEndRelOffset.readFromParcel(in);
+            mEndParentSize.readFromParcel(in);
             mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
             mAllowEnterPip = in.readBoolean();
             mStartDisplayId = in.readInt();
@@ -721,6 +723,7 @@
             out.mStartAbsBounds.set(mStartAbsBounds);
             out.mEndAbsBounds.set(mEndAbsBounds);
             out.mEndRelOffset.set(mEndRelOffset);
+            out.mEndParentSize.set(mEndParentSize);
             out.mTaskInfo = mTaskInfo;
             out.mAllowEnterPip = mAllowEnterPip;
             out.mStartDisplayId = mStartDisplayId;
@@ -781,6 +784,13 @@
         }
 
         /**
+         * Sets the size of its parent container after the change.
+         */
+        public void setEndParentSize(int width, int height) {
+            mEndParentSize.set(width, height);
+        }
+
+        /**
          * Sets the taskinfo of this container if this is a task. WARNING: this takes the
          * reference, so don't modify it afterwards.
          */
@@ -916,6 +926,14 @@
             return mEndRelOffset;
         }
 
+        /**
+         * Returns the size of parent container after the change.
+         */
+        @NonNull
+        public Point getEndParentSize() {
+            return mEndParentSize;
+        }
+
         /** @return the leash or surface to animate for this container */
         @NonNull
         public SurfaceControl getLeash() {
@@ -1003,6 +1021,7 @@
             mStartAbsBounds.writeToParcel(dest, flags);
             mEndAbsBounds.writeToParcel(dest, flags);
             mEndRelOffset.writeToParcel(dest, flags);
+            mEndParentSize.writeToParcel(dest, flags);
             dest.writeTypedObject(mTaskInfo, flags);
             dest.writeBoolean(mAllowEnterPip);
             dest.writeInt(mStartDisplayId);
@@ -1055,6 +1074,9 @@
             if (mEndRelOffset.x != 0 || mEndRelOffset.y != 0) {
                 sb.append(" eo="); sb.append(mEndRelOffset);
             }
+            if (!mEndParentSize.equals(0, 0)) {
+                sb.append(" epz=").append(mEndParentSize);
+            }
             sb.append(" d=");
             if (mStartDisplayId != mEndDisplayId) {
                 sb.append(mStartDisplayId).append("->");
@@ -1114,7 +1136,7 @@
         private final Rect mTransitionBounds = new Rect();
         private HardwareBuffer mThumbnail;
         private int mAnimations;
-        // TODO(b/295805497): Extract it from AnimationOptions
+        // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions
         private @ColorInt int mBackgroundColor;
         // Customize activity transition animation
         private CustomActivityTransition mCustomActivityOpenTransition;
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 5e88d97c..f4f6c8a 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -375,7 +375,23 @@
      */
     @NonNull
     public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) {
-        mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop));
+        return reorder(child, onTop, false /* includingParents */);
+    }
+
+    /**
+     * Reorders a container within its parent with an option to reorder all the parents in the
+     * hierarchy above among their respective siblings.
+     *
+     * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
+     *              the bottom.
+     * @param includingParents When {@code true}, all the parents in the hierarchy above are also
+     *                         reordered among their respective siblings.
+     * @hide
+     */
+    @NonNull
+    public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop,
+            boolean includingParents) {
+        mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop, includingParents));
         return this;
     }
 
@@ -1451,6 +1467,8 @@
         @Nullable
         private Rect mBounds;
 
+        private boolean mIncludingParents;
+
         private boolean mAlwaysOnTop;
 
         private boolean mReparentLeafTaskIfRelaunch;
@@ -1464,11 +1482,22 @@
                     .build();
         }
 
-        public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {
+        /**
+         * Creates the {@link HierarchyOp} for the reorder operation.
+         *
+         * @param container which needs to be reordered
+         * @param toTop if true, the container reorders
+         * @param includingParents if true, all the parents in the hierarchy above are also
+         *                         reoredered among their respective siblings
+         * @return
+         */
+        public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop,
+                boolean includingParents) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER)
                     .setContainer(container)
                     .setReparentContainer(container)
                     .setToTop(toTop)
+                    .setIncludingParents(includingParents)
                     .build();
         }
 
@@ -1555,6 +1584,7 @@
             mType = copy.mType;
             mContainer = copy.mContainer;
             mBounds = copy.mBounds;
+            mIncludingParents = copy.mIncludingParents;
             mReparent = copy.mReparent;
             mInsetsFrameProvider = copy.mInsetsFrameProvider;
             mInsetsFrameOwner = copy.mInsetsFrameOwner;
@@ -1575,6 +1605,7 @@
             mType = in.readInt();
             mContainer = in.readStrongBinder();
             mBounds = in.readTypedObject(Rect.CREATOR);
+            mIncludingParents = in.readBoolean();
             mReparent = in.readStrongBinder();
             mInsetsFrameProvider = in.readTypedObject(InsetsFrameProvider.CREATOR);
             mInsetsFrameOwner = in.readStrongBinder();
@@ -1678,6 +1709,12 @@
             return mBounds;
         }
 
+        /** Denotes whether the parents should also be included in the op. */
+        @NonNull
+        public boolean includingParents() {
+            return mIncludingParents;
+        }
+
         /** Gets a string representation of a hierarchy-op type. */
         public static String hopToString(int type) {
             switch (type) {
@@ -1789,6 +1826,7 @@
             dest.writeInt(mType);
             dest.writeStrongBinder(mContainer);
             dest.writeTypedObject(mBounds, flags);
+            dest.writeBoolean(mIncludingParents);
             dest.writeStrongBinder(mReparent);
             dest.writeTypedObject(mInsetsFrameProvider, flags);
             dest.writeStrongBinder(mInsetsFrameOwner);
@@ -1866,6 +1904,8 @@
             @Nullable
             private Rect mBounds;
 
+            private boolean mIncludingParents;
+
             private boolean mAlwaysOnTop;
 
             private boolean mReparentLeafTaskIfRelaunch;
@@ -1955,6 +1995,11 @@
                 return this;
             }
 
+            Builder setIncludingParents(boolean value) {
+                mIncludingParents = value;
+                return this;
+            }
+
             HierarchyOp build() {
                 final HierarchyOp hierarchyOp = new HierarchyOp(mType);
                 hierarchyOp.mContainer = mContainer;
@@ -1976,6 +2021,7 @@
                 hierarchyOp.mTaskFragmentOperation = mTaskFragmentOperation;
                 hierarchyOp.mShortcutInfo = mShortcutInfo;
                 hierarchyOp.mBounds = mBounds;
+                hierarchyOp.mIncludingParents = mIncludingParents;
                 hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch;
 
                 return hierarchyOp;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 0ff52f1..4fb6e69 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -37,6 +37,7 @@
 import android.view.IWindowSession;
 import android.view.ImeBackAnimationController;
 import android.view.MotionEvent;
+import android.view.ViewRootImpl;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -49,6 +50,7 @@
 import java.util.HashMap;
 import java.util.Objects;
 import java.util.TreeMap;
+import java.util.function.BooleanSupplier;
 import java.util.function.Supplier;
 
 /**
@@ -68,6 +70,7 @@
 public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
     private IWindowSession mWindowSession;
     private IWindow mWindow;
+    private ViewRootImpl mViewRoot;
     @VisibleForTesting
     public final BackTouchTracker mTouchTracker = new BackTouchTracker();
     @VisibleForTesting
@@ -134,10 +137,12 @@
      * is attached a window.
      */
     public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window,
+            @Nullable ViewRootImpl viewRoot,
             @Nullable ImeBackAnimationController imeBackAnimationController) {
         synchronized (mLock) {
             mWindowSession = windowSession;
             mWindow = window;
+            mViewRoot = viewRoot;
             mImeBackAnimationController = imeBackAnimationController;
             if (!mAllCallbacks.isEmpty()) {
                 setTopOnBackInvokedCallback(getTopCallback());
@@ -151,6 +156,7 @@
             clear();
             mWindow = null;
             mWindowSession = null;
+            mViewRoot = null;
             mImeBackAnimationController = null;
         }
     }
@@ -176,8 +182,6 @@
                 return;
             }
             if (callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) {
-                // Fall back to compat back key injection if legacy back behaviour should be used.
-                if (!isOnBackInvokedCallbackEnabled()) return;
                 if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback
                         && mImeBackAnimationController != null) {
                     // register ImeBackAnimationController instead to play predictive back animation
@@ -300,6 +304,14 @@
         }
     }
 
+    private boolean callOnKeyPreIme() {
+        if (mViewRoot != null && !isOnBackInvokedCallbackEnabled(mViewRoot.mContext)) {
+            return mViewRoot.injectBackKeyEvents(/*preImeOnly*/ true);
+        } else {
+            return false;
+        }
+    }
+
     private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
         if (mWindowSession == null || mWindow == null) {
             return;
@@ -308,8 +320,8 @@
             OnBackInvokedCallbackInfo callbackInfo = null;
             if (callback != null) {
                 int priority = mAllCallbacks.get(callback);
-                final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper(
-                        callback, mTouchTracker, mProgressAnimator, mHandler);
+                final IOnBackInvokedCallback iCallback = new OnBackInvokedCallbackWrapper(callback,
+                        mTouchTracker, mProgressAnimator, mHandler, this::callOnKeyPreIme);
                 callbackInfo = new OnBackInvokedCallbackInfo(
                         iCallback,
                         priority,
@@ -399,16 +411,20 @@
         private final BackTouchTracker mTouchTracker;
         @NonNull
         private final Handler mHandler;
+        @NonNull
+        private final BooleanSupplier mOnKeyPreIme;
 
         OnBackInvokedCallbackWrapper(
                 @NonNull OnBackInvokedCallback callback,
                 @NonNull BackTouchTracker touchTracker,
                 @NonNull BackProgressAnimator progressAnimator,
-                @NonNull Handler handler) {
+                @NonNull Handler handler,
+                @NonNull BooleanSupplier onKeyPreIme) {
             mCallback = new WeakReference<>(callback);
             mTouchTracker = touchTracker;
             mProgressAnimator = progressAnimator;
             mHandler = handler;
+            mOnKeyPreIme = onKeyPreIme;
         }
 
         @Override
@@ -435,7 +451,16 @@
         }
 
         @Override
-        public void onBackProgressed(BackMotionEvent backEvent) { }
+        public void onBackProgressed(BackMotionEvent backEvent) {
+            // This is only called in some special cases such as when activity embedding is active
+            // or when the activity is letterboxed. Otherwise mProgressAnimator#onBackProgressed is
+            // called from WindowOnBackInvokedDispatcher#onMotionEvent
+            mHandler.post(() -> {
+                if (getBackAnimationCallback() != null) {
+                    mProgressAnimator.onBackProgressed(backEvent);
+                }
+            });
+        }
 
         @Override
         public void onBackCancelled() {
@@ -451,11 +476,11 @@
         public void onBackInvoked() throws RemoteException {
             mHandler.post(() -> {
                 mTouchTracker.reset();
+                if (consumedByOnKeyPreIme()) return;
                 boolean isInProgress = mProgressAnimator.isBackAnimationInProgress();
-                mProgressAnimator.reset();
-                // TODO(b/333957271): Re-introduce auto fling progress generation.
                 final OnBackInvokedCallback callback = mCallback.get();
                 if (callback == null) {
+                    mProgressAnimator.reset();
                     Log.d(TAG, "Trying to call onBackInvoked() on a null callback reference.");
                     return;
                 }
@@ -463,10 +488,40 @@
                     Log.w(TAG, "ProgressAnimator was not in progress, skip onBackInvoked().");
                     return;
                 }
-                callback.onBackInvoked();
+                OnBackAnimationCallback animationCallback = getBackAnimationCallback();
+                if (animationCallback != null) {
+                    mProgressAnimator.onBackInvoked(callback::onBackInvoked);
+                } else {
+                    mProgressAnimator.reset();
+                    callback.onBackInvoked();
+                }
             });
         }
 
+        private boolean consumedByOnKeyPreIme() {
+            final OnBackInvokedCallback callback = mCallback.get();
+            if (callback instanceof ImeBackAnimationController
+                    || callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) {
+                // call onKeyPreIme API if the current callback is an IME callback and the app has
+                // not set enableOnBackInvokedCallback="false"
+                try {
+                    boolean consumed = mOnKeyPreIme.getAsBoolean();
+                    if (consumed) {
+                        // back event intercepted by app in onKeyPreIme -> cancel the IME animation.
+                        final OnBackAnimationCallback animationCallback =
+                                getBackAnimationCallback();
+                        if (animationCallback != null) {
+                            mProgressAnimator.onBackCancelled(animationCallback::onBackCancelled);
+                        }
+                        return true;
+                    }
+                } catch (Exception e) {
+                    Log.d(TAG, "Failed to call onKeyPreIme", e);
+                }
+            }
+            return false;
+        }
+
         @Override
         public void setTriggerBack(boolean triggerBack) throws RemoteException {
             mTouchTracker.setTriggerBack(triggerBack);
@@ -504,6 +559,7 @@
     public void setImeOnBackInvokedDispatcher(
             @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         mImeDispatcher = imeDispatcher;
+        mImeDispatcher.setHandler(mHandler);
     }
 
     /** Returns true if a non-null {@link ImeOnBackInvokedDispatcher} has been set. **/
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 1742fe3..ca125da 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -52,6 +52,13 @@
 }
 
 flag {
+    name: "enable_desktop_windowing_scvh_cache"
+    namespace: "lse_desktop_experience"
+    description: "Enables a SurfaceControlViewHost cache for window decorations"
+    bug: "345146928"
+}
+
+flag {
     name: "enable_desktop_windowing_wallpaper_activity"
     namespace: "lse_desktop_experience"
     description: "Enables desktop wallpaper activity to show wallpaper in the desktop mode"
@@ -99,3 +106,17 @@
     description: "Whether to apply Camera Compat treatment to fixed-orientation apps in desktop windowing mode"
     bug: "314952133"
 }
+
+flag {
+    name: "enable_task_stack_observer_in_shell"
+    namespace: "lse_desktop_experience"
+    description: "Introduces a new observer in shell to track the task stack."
+    bug: "341932484"
+}
+
+flag {
+    name: "enable_desktop_windowing_size_constraints"
+    namespace: "lse_desktop_experience"
+    description: "Whether to enable min/max window size constraints when resizing a window in desktop windowing mode"
+    bug: "327589741"
+}
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index 150b04e..01c78a0 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -31,4 +31,11 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
-}
\ No newline at end of file
+}
+
+flag {
+  name: "no_visibility_event_on_display_state_change"
+  namespace: "wear_frameworks"
+  description: "Prevent the system from sending visibility event on display state change."
+  bug: "331725519"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index d6f65f8..b714682 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -19,6 +19,16 @@
 }
 
 flag {
+    name: "blast_sync_notification_shade_on_display_switch"
+    namespace: "windowing_frontend"
+    description: "Make the buffer content of notification shade synchronize with display switch"
+    bug: "337154331"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
   name: "edge_to_edge_by_default"
   namespace: "windowing_frontend"
   description: "Make app go edge-to-edge by default when targeting SDK 35 or greater"
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 87ede4a..0a4762d 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -180,3 +180,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    namespace: "windowing_sdk"
+    name: "rear_display_disable_force_desktop_system_decorations"
+    description: "Block system decorations from being added to a rear display when desktop mode is forced"
+    bug: "346103150"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 98d6ec6..920981e 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -923,7 +923,7 @@
         mSystemWindowInsets = insets.getSystemWindowInsets();
 
         mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
-                mSystemWindowInsets.right, mSystemWindowInsets.bottom);
+                mSystemWindowInsets.right, 0);
 
         resetButtonBar();
 
@@ -952,7 +952,7 @@
 
         if (mSystemWindowInsets != null) {
             mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
-                    mSystemWindowInsets.right, mSystemWindowInsets.bottom);
+                    mSystemWindowInsets.right, 0);
         }
     }
 
diff --git a/core/java/com/android/internal/dynamicanimation/animation/FlingAnimation.java b/core/java/com/android/internal/dynamicanimation/animation/FlingAnimation.java
new file mode 100644
index 0000000..2bd0568
--- /dev/null
+++ b/core/java/com/android/internal/dynamicanimation/animation/FlingAnimation.java
@@ -0,0 +1,203 @@
+/*
+ * 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.dynamicanimation.animation;
+
+import android.annotation.FloatRange;
+/**
+ * <p>Fling animation is an animation that continues an initial momentum (most often from gesture
+ * velocity) and gradually slows down. The fling animation will come to a stop when the velocity of
+ * the animation is below the threshold derived from {@link #setMinimumVisibleChange(float)},
+ * or when the value of the animation has gone beyond the min or max value defined via
+ * {@link DynamicAnimation#setMinValue(float)} or {@link DynamicAnimation#setMaxValue(float)}.
+ * It is recommended to restrict the fling animation with min and/or max value, such that the
+ * animation can end when it goes beyond screen bounds, thus preserving CPU cycles and resources.
+ *
+ * <p>For example, you can create a fling animation that animates the translationX of a view:
+ * <pre class="prettyprint">
+ * FlingAnimation flingAnim = new FlingAnimation(view, DynamicAnimation.TRANSLATION_X)
+ *         // Sets the start velocity to -2000 (pixel/s)
+ *         .setStartVelocity(-2000)
+ *         // Optional but recommended to set a reasonable min and max range for the animation.
+ *         // In this particular case, we set the min and max to -200 and 2000 respectively.
+ *         .setMinValue(-200).setMaxValue(2000);
+ * flingAnim.start();
+ * </pre>
+ */
+public final class FlingAnimation extends DynamicAnimation<FlingAnimation> {
+    private final DragForce mFlingForce = new DragForce();
+    /**
+     * <p>This creates a FlingAnimation that animates a {@link FloatValueHolder} instance. During
+     * the animation, the {@link FloatValueHolder} instance will be updated via
+     * {@link FloatValueHolder#setValue(float)} each frame. The caller can obtain the up-to-date
+     * animation value via {@link FloatValueHolder#getValue()}.
+     *
+     * <p><strong>Note:</strong> changing the value in the {@link FloatValueHolder} via
+     * {@link FloatValueHolder#setValue(float)} outside of the animation during an
+     * animation run will not have any effect on the on-going animation.
+     *
+     * @param floatValueHolder the property to be animated
+     */
+    public FlingAnimation(FloatValueHolder floatValueHolder) {
+        super(floatValueHolder);
+        mFlingForce.setValueThreshold(getValueThreshold());
+    }
+    /**
+     * Sets the friction for the fling animation. The greater the friction is, the sooner the
+     * animation will slow down. When not set, the friction defaults to 1.
+     *
+     * @param friction the friction used in the animation
+     * @return the animation whose friction will be scaled
+     * @throws IllegalArgumentException if the input friction is not positive
+     */
+    public FlingAnimation setFriction(
+            @FloatRange(from = 0.0, fromInclusive = false) float friction) {
+        if (friction <= 0) {
+            throw new IllegalArgumentException("Friction must be positive");
+        }
+        mFlingForce.setFrictionScalar(friction);
+        return this;
+    }
+    /**
+     * Returns the friction being set on the animation via {@link #setFriction(float)}. If the
+     * friction has not been set, the default friction of 1 will be returned.
+     *
+     * @return friction being used in the animation
+     */
+    public float getFriction() {
+        return mFlingForce.getFrictionScalar();
+    }
+    /**
+     * Sets the min value of the animation. When a fling animation reaches the min value, the
+     * animation will end immediately. Animations will not animate beyond the min value.
+     *
+     * @param minValue minimum value of the property to be animated
+     * @return the Animation whose min value is being set
+     */
+    @Override
+    public FlingAnimation setMinValue(float minValue) {
+        super.setMinValue(minValue);
+        return this;
+    }
+    /**
+     * Sets the max value of the animation. When a fling animation reaches the max value, the
+     * animation will end immediately. Animations will not animate beyond the max value.
+     *
+     * @param maxValue maximum value of the property to be animated
+     * @return the Animation whose max value is being set
+     */
+    @Override
+    public FlingAnimation setMaxValue(float maxValue) {
+        super.setMaxValue(maxValue);
+        return this;
+    }
+    /**
+     * Start velocity of the animation. Default velocity is 0. Unit: pixel/second
+     *
+     * <p>A <b>non-zero</b> start velocity is required for a FlingAnimation. If no start velocity is
+     * set through {@link #setStartVelocity(float)}, the start velocity defaults to 0. In that
+     * case, the fling animation will consider itself done in the next frame.
+     *
+     * <p>Note when using a fixed value as the start velocity (as opposed to getting the velocity
+     * through touch events), it is recommended to define such a value in dp/second and convert it
+     * to pixel/second based on the density of the screen to achieve a consistent look across
+     * different screens.
+     *
+     * <p>To convert from dp/second to pixel/second:
+     * <pre class="prettyprint">
+     * float pixelPerSecond = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpPerSecond,
+     *         getResources().getDisplayMetrics());
+     * </pre>
+     *
+     * @param startVelocity start velocity of the animation in pixel/second
+     * @return the Animation whose start velocity is being set
+     */
+    @Override
+    public FlingAnimation setStartVelocity(float startVelocity) {
+        super.setStartVelocity(startVelocity);
+        return this;
+    }
+    @Override
+    boolean updateValueAndVelocity(long deltaT) {
+        MassState state = mFlingForce.updateValueAndVelocity(mValue, mVelocity, deltaT);
+        mValue = state.mValue;
+        mVelocity = state.mVelocity;
+        // When the animation hits the max/min value, consider animation done.
+        if (mValue < mMinValue) {
+            mValue = mMinValue;
+            return true;
+        }
+        if (mValue > mMaxValue) {
+            mValue = mMaxValue;
+            return true;
+        }
+        if (isAtEquilibrium(mValue, mVelocity)) {
+            return true;
+        }
+        return false;
+    }
+    @Override
+    float getAcceleration(float value, float velocity) {
+        return mFlingForce.getAcceleration(value, velocity);
+    }
+    @Override
+    boolean isAtEquilibrium(float value, float velocity) {
+        return value >= mMaxValue
+                || value <= mMinValue
+                || mFlingForce.isAtEquilibrium(value, velocity);
+    }
+    @Override
+    void setValueThreshold(float threshold) {
+        mFlingForce.setValueThreshold(threshold);
+    }
+    private static final class DragForce implements Force {
+        private static final float DEFAULT_FRICTION = -4.2f;
+        // This multiplier is used to calculate the velocity threshold given a certain value
+        // threshold. The idea is that if it takes >= 1 frame to move the value threshold amount,
+        // then the velocity is a reasonable threshold.
+        private static final float VELOCITY_THRESHOLD_MULTIPLIER = 1000f / 16f;
+        private float mFriction = DEFAULT_FRICTION;
+        private float mVelocityThreshold;
+        // Internal state to hold a value/velocity pair.
+        private final DynamicAnimation.MassState mMassState = new DynamicAnimation.MassState();
+        void setFrictionScalar(float frictionScalar) {
+            mFriction = frictionScalar * DEFAULT_FRICTION;
+        }
+        float getFrictionScalar() {
+            return mFriction / DEFAULT_FRICTION;
+        }
+        MassState updateValueAndVelocity(float value, float velocity, long deltaT) {
+            mMassState.mVelocity = (float) (velocity * Math.exp((deltaT / 1000f) * mFriction));
+            mMassState.mValue = (float) (value - velocity / mFriction
+                    + velocity / mFriction * Math.exp(mFriction * deltaT / 1000f));
+            if (isAtEquilibrium(mMassState.mValue, mMassState.mVelocity)) {
+                mMassState.mVelocity = 0f;
+            }
+            return mMassState;
+        }
+        @Override
+        public float getAcceleration(float position, float velocity) {
+            return velocity * mFriction;
+        }
+        @Override
+        public boolean isAtEquilibrium(float value, float velocity) {
+            return Math.abs(velocity) < mVelocityThreshold;
+        }
+        void setValueThreshold(float threshold) {
+            mVelocityThreshold = threshold * VELOCITY_THRESHOLD_MULTIPLIER;
+        }
+    }
+}
+
diff --git a/core/java/com/android/internal/inputmethod/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
index ebae39e..0eaf02b 100644
--- a/core/java/com/android/internal/inputmethod/IImeTracker.aidl
+++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
@@ -18,6 +18,8 @@
 
 import android.view.inputmethod.ImeTracker;
 
+import com.android.internal.infra.AndroidFuture;
+
 /**
  * Interface to the global IME tracker service, used by all client applications.
  * {@hide}
@@ -98,9 +100,12 @@
     /**
      * Finishes the tracking of any pending IME visibility requests. This won't stop the actual
      * requests, but allows resetting the state when starting up test runs.
+     *
+     * @param completionSignal used to signal when the tracking has been finished.
      */
     @EnforcePermission("TEST_INPUT_METHOD")
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
             + "android.Manifest.permission.TEST_INPUT_METHOD)")
-    oneway void finishTrackingPendingImeVisibilityRequests();
+    oneway void finishTrackingPendingImeVisibilityRequests(
+        in AndroidFuture completionSignal /* T=Void */);
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 244165f..5c270e0 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -1514,6 +1514,36 @@
     }
 
     /**
+     * Records an event when some state2 flag changes to true.
+     */
+    public void recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags,
+            int uid, String name) {
+        synchronized (this) {
+            mHistoryCur.states2 |= stateFlags;
+            mHistoryCur.eventCode = EVENT_STATE_CHANGE | EVENT_FLAG_START;
+            mHistoryCur.eventTag = mHistoryCur.localEventTag;
+            mHistoryCur.eventTag.uid = uid;
+            mHistoryCur.eventTag.string = name;
+            writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+        }
+    }
+
+    /**
+     * Records an event when some state2 flag changes to false.
+     */
+    public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags,
+            int uid, String name) {
+        synchronized (this) {
+            mHistoryCur.states2 &= ~stateFlags;
+            mHistoryCur.eventCode = EVENT_STATE_CHANGE | EVENT_FLAG_FINISH;
+            mHistoryCur.eventTag = mHistoryCur.localEventTag;
+            mHistoryCur.eventTag.uid = uid;
+            mHistoryCur.eventTag.string = name;
+            writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+        }
+    }
+
+    /**
      * Records an event when some state2 flag changes to false.
      */
     public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) {
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 996e424..391d257 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -12,3 +12,6 @@
 per-file *PowerProfile* = file:/BATTERY_STATS_OWNERS
 per-file *PowerStats* = file:/BATTERY_STATS_OWNERS
 
+# ANRs
+# Bug component : 158088 = per-file TimeoutRecord.java
+per-file TimeoutRecord.java = file:/PERFORMANCE_OWNERS
diff --git a/core/java/com/android/internal/os/TimeoutRecord.java b/core/java/com/android/internal/os/TimeoutRecord.java
index e9a8d4b..9306956 100644
--- a/core/java/com/android/internal/os/TimeoutRecord.java
+++ b/core/java/com/android/internal/os/TimeoutRecord.java
@@ -80,6 +80,9 @@
     /** Latency tracker associated with this instance. */
     public final AnrLatencyTracker mLatencyTracker;
 
+    /** A handle to the timer that expired.  A value of null means "no timer". */
+    private AutoCloseable mExpiredTimer;
+
     private TimeoutRecord(@TimeoutKind int kind, @NonNull String reason, long endUptimeMillis,
             boolean endTakenBeforeLocks) {
         this.mKind = kind;
@@ -87,6 +90,7 @@
         this.mEndUptimeMillis = endUptimeMillis;
         this.mEndTakenBeforeLocks = endTakenBeforeLocks;
         this.mLatencyTracker = new AnrLatencyTracker(kind, endUptimeMillis);
+        this.mExpiredTimer = null;
     }
 
     private static TimeoutRecord endingNow(@TimeoutKind int kind, String reason) {
@@ -197,4 +201,22 @@
     public static TimeoutRecord forAppStart(String reason) {
         return TimeoutRecord.endingNow(TimeoutKind.APP_START, reason);
     }
+
+    /** Record the ID of the timer that expired. */
+    @NonNull
+    public TimeoutRecord setExpiredTimer(@Nullable AutoCloseable handle) {
+        mExpiredTimer = handle;
+        return this;
+    }
+
+    /** Close the ExpiredTimer, if one is present. */
+    public void closeExpiredTimer() {
+        try {
+            if (mExpiredTimer != null) mExpiredTimer.close();
+        } catch (Exception e) {
+            // mExpiredTimer.close() should never, ever throw.  If it does, just rethrow as a
+            // RuntimeException.
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index c14a6c1..63ff598 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1227,7 +1227,10 @@
                     requestApplyInsets();
                 }
             }
-            if (insets != null) {
+            if (insets != null && (consumedLeft > 0
+                    || consumedTop > 0
+                    || consumedRight > 0
+                    || consumedBottom > 0)) {
                 insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
             }
         }
diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
index f1ed3be..b7e68ba 100644
--- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -36,11 +36,13 @@
     private Context mContext;
     private Runnable mOnChangeRunnable;
     private Handler mMainHandler;
+    private Handler mBgHandler;
 
-    public GestureNavigationSettingsObserver(Handler handler, Context context,
-            Runnable onChangeRunnable) {
-        super(handler);
-        mMainHandler = handler;
+    public GestureNavigationSettingsObserver(
+            Handler mainHandler, Handler bgHandler, Context context, Runnable onChangeRunnable) {
+        super(mainHandler);
+        mMainHandler = mainHandler;
+        mBgHandler = bgHandler;
         mContext = context;
         mOnChangeRunnable = onChangeRunnable;
     }
@@ -60,45 +62,51 @@
      * Registers the observer for all users.
      */
     public void register() {
-        ContentResolver r = mContext.getContentResolver();
-        r.registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
-                false, this, UserHandle.USER_ALL);
-        r.registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
-                false, this, UserHandle.USER_ALL);
-        r.registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
-                false, this, UserHandle.USER_ALL);
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                runnable -> mMainHandler.post(runnable),
-                mOnPropertiesChangedListener);
+        mBgHandler.post(() -> {
+            ContentResolver r = mContext.getContentResolver();
+            r.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
+                    false, this, UserHandle.USER_ALL);
+            r.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
+                    false, this, UserHandle.USER_ALL);
+            r.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
+                    false, this, UserHandle.USER_ALL);
+            DeviceConfig.addOnPropertiesChangedListener(
+                    DeviceConfig.NAMESPACE_SYSTEMUI,
+                    runnable -> mMainHandler.post(runnable),
+                    mOnPropertiesChangedListener);
+        });
     }
 
     /**
      * Registers the observer for the calling user.
      */
     public void registerForCallingUser() {
-        ContentResolver r = mContext.getContentResolver();
-        r.registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
-                false, this);
-        r.registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
-                false, this);
-        r.registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
-                false, this);
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                runnable -> mMainHandler.post(runnable),
-                mOnPropertiesChangedListener);
+        mBgHandler.post(() -> {
+            ContentResolver r = mContext.getContentResolver();
+            r.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT),
+                    false, this);
+            r.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT),
+                    false, this);
+            r.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE),
+                    false, this);
+            DeviceConfig.addOnPropertiesChangedListener(
+                    DeviceConfig.NAMESPACE_SYSTEMUI,
+                    runnable -> mMainHandler.post(runnable),
+                    mOnPropertiesChangedListener);
+        });
     }
 
     public void unregister() {
-        mContext.getContentResolver().unregisterContentObserver(this);
-        DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+        mBgHandler.post(() -> {
+            mContext.getContentResolver().unregisterContentObserver(this);
+            DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
+        });
     }
 
     @Override
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 2194c89..42be4fc 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -537,8 +537,13 @@
         }
         mContentParent.requestApplyInsets();
         final Callback cb = getCallback();
-        if (cb != null && !isDestroyed()) {
-            cb.onContentChanged();
+        if (!isDestroyed()) {
+            if (cb != null) {
+                cb.onContentChanged();
+            }
+            if (mDecorContentParent != null) {
+                mDecorContentParent.notifyContentChanged();
+            }
         }
         mContentParentExplicitlySet = true;
     }
@@ -568,8 +573,13 @@
         }
         mContentParent.requestApplyInsets();
         final Callback cb = getCallback();
-        if (cb != null && !isDestroyed()) {
-            cb.onContentChanged();
+        if (!isDestroyed()) {
+            if (cb != null) {
+                cb.onContentChanged();
+            }
+            if (mDecorContentParent != null) {
+                mDecorContentParent.notifyContentChanged();
+            }
         }
         mContentParentExplicitlySet = true;
     }
@@ -586,8 +596,13 @@
         mContentParent.addView(view, params);
         mContentParent.requestApplyInsets();
         final Callback cb = getCallback();
-        if (cb != null && !isDestroyed()) {
-            cb.onContentChanged();
+        if (!isDestroyed()) {
+            if (cb != null) {
+                cb.onContentChanged();
+            }
+            if (mDecorContentParent != null) {
+                mDecorContentParent.notifyContentChanged();
+            }
         }
     }
 
@@ -4054,10 +4069,7 @@
     }
 
     @Override
-    public void setResizingCaptionDrawable(Drawable drawable) {
-        // TODO(b/333724879): Deprecate this public API. The new caption in WM shell allows the app
-        // content to draw behind it directly if requested.
-    }
+    public void setResizingCaptionDrawable(Drawable drawable) {}
 
     @Override
     public void setDecorCaptionShade(int decorCaptionShade) {
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java
index 4f80afa..76ce452 100644
--- a/core/java/com/android/internal/statusbar/StatusBarIcon.java
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java
@@ -22,7 +22,21 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 
+import androidx.annotation.NonNull;
+
 public class StatusBarIcon implements Parcelable {
+    public enum Type {
+        // Notification: the sender avatar for important conversations
+        PeopleAvatar,
+        // Notification: the monochrome version of the app icon if available; otherwise fall back to
+        // the small icon
+        MaybeMonochromeAppIcon,
+        // Notification: the small icon from the notification
+        NotifSmallIcon,
+        // The wi-fi, cellular or battery icon.
+        SystemIcon
+    }
+
     public UserHandle user;
     public String pkg;
     public Icon icon;
@@ -30,9 +44,10 @@
     public boolean visible = true;
     public int number;
     public CharSequence contentDescription;
+    public Type type;
 
     public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
-            CharSequence contentDescription) {
+            CharSequence contentDescription, Type type) {
         if (icon.getType() == Icon.TYPE_RESOURCE
                 && TextUtils.isEmpty(icon.getResPackage())) {
             // This is an odd situation where someone's managed to hand us an icon without a
@@ -46,15 +61,17 @@
         this.iconLevel = iconLevel;
         this.number = number;
         this.contentDescription = contentDescription;
+        this.type = type;
     }
 
     public StatusBarIcon(String iconPackage, UserHandle user,
             int iconId, int iconLevel, int number,
-            CharSequence contentDescription) {
+            CharSequence contentDescription, Type type) {
         this(user, iconPackage, Icon.createWithResource(iconPackage, iconId),
-                iconLevel, number, contentDescription);
+                iconLevel, number, contentDescription, type);
     }
 
+    @NonNull
     @Override
     public String toString() {
         return "StatusBarIcon(icon=" + icon
@@ -65,10 +82,11 @@
                 + " )";
     }
 
+    @NonNull
     @Override
     public StatusBarIcon clone() {
         StatusBarIcon that = new StatusBarIcon(this.user, this.pkg, this.icon,
-                this.iconLevel, this.number, this.contentDescription);
+                this.iconLevel, this.number, this.contentDescription, this.type);
         that.visible = this.visible;
         return that;
     }
@@ -88,6 +106,7 @@
         this.visible = in.readInt() != 0;
         this.number = in.readInt();
         this.contentDescription = in.readCharSequence();
+        this.type = Type.valueOf(in.readString());
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -98,6 +117,7 @@
         out.writeInt(this.visible ? 1 : 0);
         out.writeInt(this.number);
         out.writeCharSequence(this.contentDescription);
+        out.writeString(this.type.name());
     }
 
     public int describeContents() {
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
old mode 100755
new mode 100644
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
old mode 100755
new mode 100644
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
old mode 100755
new mode 100644
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 707f109..ff57fd4 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -52,6 +52,7 @@
  */
 public class ActionBarOverlayLayout extends ViewGroup implements DecorContentParent {
     private static final String TAG = "ActionBarOverlayLayout";
+    private static final Rect EMPTY_RECT = new Rect();
 
     private int mActionBarHeight;
     //private WindowDecorActionBar mActionBar;
@@ -294,55 +295,53 @@
     }
 
     private boolean applyInsets(View view, Rect insets, boolean toPadding,
-            boolean left, boolean top, boolean bottom, boolean right) {
+            boolean left, boolean top, boolean right, boolean bottom) {
         boolean changed;
         if (toPadding) {
-            changed = setMargin(view, left, top, bottom, right, 0, 0, 0, 0);
-            changed |= setPadding(view, left, top, bottom, right,
-                    insets.left, insets.top, insets.right, insets.bottom);
+            changed = setMargin(view, EMPTY_RECT, left, top, right, bottom);
+            changed |= setPadding(view, insets, left, top, right, bottom);
         } else {
-            changed = setPadding(view, left, top, bottom, right, 0, 0, 0, 0);
-            changed |= setMargin(view, left, top, bottom, right,
-                    insets.left, insets.top, insets.right, insets.bottom);
+            changed = setPadding(view, EMPTY_RECT, left, top, right, bottom);
+            changed |= setMargin(view, insets, left, top, right, bottom);
         }
         return changed;
     }
 
-    private boolean setPadding(View view, boolean left, boolean top, boolean bottom, boolean right,
-            int l, int t, int r, int b) {
-        if ((left && view.getPaddingLeft() != l)
-                || (top && view.getPaddingTop() != t)
-                || (right && view.getPaddingRight() != r)
-                || (bottom && view.getPaddingBottom() != b)) {
+    private boolean setPadding(View view, Rect insets,
+            boolean left, boolean top, boolean right, boolean bottom) {
+        if ((left && view.getPaddingLeft() != insets.left)
+                || (top && view.getPaddingTop() != insets.top)
+                || (right && view.getPaddingRight() != insets.right)
+                || (bottom && view.getPaddingBottom() != insets.bottom)) {
             view.setPadding(
-                    left ? l : view.getPaddingLeft(),
-                    top ? t : view.getPaddingTop(),
-                    right ? r : view.getPaddingRight(),
-                    bottom ? b : view.getPaddingBottom());
+                    left ? insets.left : view.getPaddingLeft(),
+                    top ? insets.top : view.getPaddingTop(),
+                    right ? insets.right : view.getPaddingRight(),
+                    bottom ? insets.bottom : view.getPaddingBottom());
             return true;
         }
         return false;
     }
 
-    private boolean setMargin(View view, boolean left, boolean top, boolean bottom, boolean right,
-            int l, int t, int r, int b) {
-        LayoutParams lp = (LayoutParams) view.getLayoutParams();
+    private boolean setMargin(View view,  Rect insets,
+            boolean left, boolean top, boolean right, boolean bottom) {
+        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
         boolean changed = false;
-        if (left && lp.leftMargin != l) {
+        if (left && lp.leftMargin != insets.left) {
             changed = true;
-            lp.leftMargin = l;
+            lp.leftMargin = insets.left;
         }
-        if (top && lp.topMargin != t) {
+        if (top && lp.topMargin != insets.top) {
             changed = true;
-            lp.topMargin = t;
+            lp.topMargin = insets.top;
         }
-        if (right && lp.rightMargin != r) {
+        if (right && lp.rightMargin != insets.right) {
             changed = true;
-            lp.rightMargin = r;
+            lp.rightMargin = insets.right;
         }
-        if (bottom && lp.bottomMargin != b) {
+        if (bottom && lp.bottomMargin != insets.bottom) {
             changed = true;
-            lp.bottomMargin = b;
+            lp.bottomMargin = insets.bottom;
         }
         return changed;
     }
@@ -370,7 +369,7 @@
 
         // The top and bottom action bars are always within the content area.
         boolean changed = applyInsets(mActionBarTop, mSystemInsets,
-                mActionBarExtendsIntoSystemInsets, true, true, false, true);
+                mActionBarExtendsIntoSystemInsets, true, true, true, false);
         if (mActionBarBottom != null) {
             changed |= applyInsets(mActionBarBottom, mSystemInsets,
                     mActionBarExtendsIntoSystemInsets, true, false, true, true);
@@ -522,7 +521,7 @@
                 );
             }
         }
-        applyInsets(mContent, mContentInsets, false /* toPadding */, true, true, true, true);
+        setMargin(mContent, mContentInsets, true, true, true, true);
 
         if (!mLastInnerInsets.equals(mInnerInsets)) {
             // If the inner insets have changed, we need to dispatch this down to
@@ -899,6 +898,13 @@
         mDecorToolbar.dismissPopupMenus();
     }
 
+    @Override
+    public void notifyContentChanged() {
+        mLastBaseContentInsets.setEmpty();
+        mLastBaseInnerInsets = WindowInsets.CONSUMED;
+        mLastInnerInsets = WindowInsets.CONSUMED;
+    }
+
     public static class LayoutParams extends MarginLayoutParams {
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index bd654fa..2bfbf84 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.widget;
 
+import static android.widget.flags.Flags.conversationLayoutUseMaximumChildHeight;
+
 import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL;
 import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_INLINE;
 
@@ -1407,6 +1409,38 @@
         }
     }
 
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // ConversationLayout needs to set its height to its biggest child to show the content
+        // properly.
+        // FrameLayout measures its match_parent children twice when any of FLs dimension is not
+        // specified. However, its sets its own dimensions before the second measurement pass.
+        // Content CutOff happens when children have bigger height on its second measurement.
+        if (conversationLayoutUseMaximumChildHeight()) {
+            int maxHeight = getMeasuredHeight();
+            final int count = getChildCount();
+
+            for (int i = 0; i < count; i++) {
+                final View child = getChildAt(i);
+                if (child == null || child.getVisibility() == GONE) {
+                    continue;
+                }
+
+                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                maxHeight = Math.max(maxHeight,
+                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
+            }
+
+            maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+            if (maxHeight != getMeasuredHeight()) {
+                setMeasuredDimension(getMeasuredWidth(), maxHeight);
+            }
+        }
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
diff --git a/core/java/com/android/internal/widget/DecorContentParent.java b/core/java/com/android/internal/widget/DecorContentParent.java
index ac524f9..8d6cfd1 100644
--- a/core/java/com/android/internal/widget/DecorContentParent.java
+++ b/core/java/com/android/internal/widget/DecorContentParent.java
@@ -22,6 +22,7 @@
 import android.util.SparseArray;
 import android.view.Menu;
 import android.view.Window;
+
 import com.android.internal.view.menu.MenuPresenter;
 
 /**
@@ -49,4 +50,5 @@
     void saveToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
     void restoreToolbarHierarchyState(SparseArray<Parcelable> toolbarStates);
     void dismissPopups();
+    void notifyContentChanged();
 }
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 58bddae..f5f04a7 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -22,7 +22,12 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
 import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
@@ -36,6 +41,13 @@
 @RemoteViews.RemoteView
 public class NotificationRowIconView extends CachingIconView {
     private boolean mApplyCircularCrop = false;
+    private boolean mShouldShowAppIcon = false;
+
+    // Padding and background set on the view prior to being changed by setShouldShowAppIcon(true),
+    // to be restored if shouldShowAppIcon becomes false again.
+    private Rect mOriginalPadding = null;
+    private Drawable mOriginalBackground = null;
+
 
     public NotificationRowIconView(Context context) {
         super(context);
@@ -59,7 +71,7 @@
     @Override
     protected void onFinishInflate() {
         // If showing the app icon, we don't need background or padding.
-        if (Flags.notificationsUseAppIcon() || Flags.notificationsUseAppIconInRow()) {
+        if (Flags.notificationsUseAppIcon()) {
             setPadding(0, 0, 0, 0);
             setBackground(null);
         }
@@ -67,6 +79,42 @@
         super.onFinishInflate();
     }
 
+    /** Whether the icon represents the app icon (instead of the small icon). */
+    @RemotableViewMethod
+    public void setShouldShowAppIcon(boolean shouldShowAppIcon) {
+        if (Flags.notificationsUseAppIconInRow()) {
+            if (mShouldShowAppIcon == shouldShowAppIcon) {
+                return; // no change
+            }
+
+            mShouldShowAppIcon = shouldShowAppIcon;
+            if (mShouldShowAppIcon) {
+                if (mOriginalPadding == null && mOriginalBackground == null) {
+                    mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(),
+                            getPaddingRight(), getPaddingBottom());
+                    mOriginalBackground = getBackground();
+                }
+
+                setPadding(0, 0, 0, 0);
+
+                // Make the background white in case the icon itself doesn't have one.
+                int white = Color.rgb(255, 255, 255);
+                ColorFilter colorFilter = new PorterDuffColorFilter(white,
+                        PorterDuff.Mode.SRC_ATOP);
+                getBackground().mutate().setColorFilter(colorFilter);
+            } else {
+                // Restore original padding and background if needed
+                if (mOriginalPadding != null) {
+                    setPadding(mOriginalPadding.left, mOriginalPadding.top, mOriginalPadding.right,
+                            mOriginalPadding.bottom);
+                    mOriginalPadding = null;
+                }
+                setBackground(mOriginalBackground);
+                mOriginalBackground = null;
+            }
+        }
+    }
+
     @Nullable
     @Override
     Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 55f2dee..00262be 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -30,7 +30,7 @@
 
     ArrayList<Operation> mOperations;
     RemoteComposeState mRemoteComposeState = new RemoteComposeState();
-
+    TimeVariables mTimeVariables = new TimeVariables();
     // Semantic version of the document
     Version mVersion = new Version(0, 1, 0);
 
@@ -70,6 +70,7 @@
 
     public void setWidth(int width) {
         this.mWidth = width;
+        mRemoteComposeState.setWindowWidth(width);
     }
 
     public int getHeight() {
@@ -78,6 +79,8 @@
 
     public void setHeight(int height) {
         this.mHeight = height;
+        mRemoteComposeState.setWindowHeight(height);
+
     }
 
     public RemoteComposeBuffer getBuffer() {
@@ -111,21 +114,21 @@
     /**
      * Sets the way the player handles the content
      *
-     * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+     * @param scroll    set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
      * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
-     * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
-     * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
-     *             the LAYOUT modes are:
-     *             - LAYOUT_MATCH_PARENT
-     *             - LAYOUT_WRAP_CONTENT
-     *             or adding an horizontal mode and a vertical mode:
-     *             - LAYOUT_HORIZONTAL_MATCH_PARENT
-     *             - LAYOUT_HORIZONTAL_WRAP_CONTENT
-     *             - LAYOUT_HORIZONTAL_FIXED
-     *             - LAYOUT_VERTICAL_MATCH_PARENT
-     *             - LAYOUT_VERTICAL_WRAP_CONTENT
-     *             - LAYOUT_VERTICAL_FIXED
-     *             The LAYOUT_*_FIXED modes will use the intrinsic document size
+     * @param sizing    set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+     * @param mode      set the mode of sizing, either LAYOUT modes or SCALE modes
+     *                  the LAYOUT modes are:
+     *                  - LAYOUT_MATCH_PARENT
+     *                  - LAYOUT_WRAP_CONTENT
+     *                  or adding an horizontal mode and a vertical mode:
+     *                  - LAYOUT_HORIZONTAL_MATCH_PARENT
+     *                  - LAYOUT_HORIZONTAL_WRAP_CONTENT
+     *                  - LAYOUT_HORIZONTAL_FIXED
+     *                  - LAYOUT_VERTICAL_MATCH_PARENT
+     *                  - LAYOUT_VERTICAL_WRAP_CONTENT
+     *                  - LAYOUT_VERTICAL_FIXED
+     *                  The LAYOUT_*_FIXED modes will use the intrinsic document size
      */
     public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
         this.mContentScroll = scroll;
@@ -138,8 +141,8 @@
      * Given dimensions w x h of where to paint the content, returns the corresponding scale factor
      * according to the contentSizing information
      *
-     * @param w horizontal dimension of the rendering area
-     * @param h vertical dimension of the rendering area
+     * @param w           horizontal dimension of the rendering area
+     * @param h           vertical dimension of the rendering area
      * @param scaleOutput will contain the computed scale factor
      */
     public void computeScale(float w, float h, float[] scaleOutput) {
@@ -154,37 +157,43 @@
                     float scale = Math.min(1f, Math.min(scaleX, scaleY));
                     contentScaleX = scale;
                     contentScaleY = scale;
-                } break;
+                }
+                break;
                 case RootContentBehavior.SCALE_FIT: {
                     float scaleX = w / mWidth;
                     float scaleY = h / mHeight;
                     float scale = Math.min(scaleX, scaleY);
                     contentScaleX = scale;
                     contentScaleY = scale;
-                } break;
+                }
+                break;
                 case RootContentBehavior.SCALE_FILL_WIDTH: {
                     float scale = w / mWidth;
                     contentScaleX = scale;
                     contentScaleY = scale;
-                } break;
+                }
+                break;
                 case RootContentBehavior.SCALE_FILL_HEIGHT: {
                     float scale = h / mHeight;
                     contentScaleX = scale;
                     contentScaleY = scale;
-                } break;
+                }
+                break;
                 case RootContentBehavior.SCALE_CROP: {
                     float scaleX = w / mWidth;
                     float scaleY = h / mHeight;
                     float scale = Math.max(scaleX, scaleY);
                     contentScaleX = scale;
                     contentScaleY = scale;
-                } break;
+                }
+                break;
                 case RootContentBehavior.SCALE_FILL_BOUNDS: {
                     float scaleX = w / mWidth;
                     float scaleY = h / mHeight;
                     contentScaleX = scaleX;
                     contentScaleY = scaleY;
-                } break;
+                }
+                break;
                 default:
                     // nothing
             }
@@ -197,10 +206,10 @@
      * Given dimensions w x h of where to paint the content, returns the corresponding translation
      * according to the contentAlignment information
      *
-     * @param w horizontal dimension of the rendering area
-     * @param h vertical dimension of the rendering area
-     * @param contentScaleX the horizontal scale we are going to use for the content
-     * @param contentScaleY the vertical scale we are going to use for the content
+     * @param w               horizontal dimension of the rendering area
+     * @param h               vertical dimension of the rendering area
+     * @param contentScaleX   the horizontal scale we are going to use for the content
+     * @param contentScaleY   the vertical scale we are going to use for the content
      * @param translateOutput will contain the computed translation
      */
     private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY,
@@ -215,26 +224,32 @@
         switch (horizontalContentAlignment) {
             case RootContentBehavior.ALIGNMENT_START: {
                 // nothing
-            } break;
+            }
+            break;
             case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: {
                 translateX = (w - contentWidth) / 2f;
-            } break;
+            }
+            break;
             case RootContentBehavior.ALIGNMENT_END: {
                 translateX = w - contentWidth;
-            } break;
+            }
+            break;
             default:
                 // nothing (same as alignment_start)
         }
         switch (verticalContentAlignment) {
             case RootContentBehavior.ALIGNMENT_TOP: {
                 // nothing
-            } break;
+            }
+            break;
             case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: {
                 translateY = (h - contentHeight) / 2f;
-            } break;
+            }
+            break;
             case RootContentBehavior.ALIGNMENT_BOTTOM: {
                 translateY = h - contentHeight;
-            } break;
+            }
+            break;
             default:
                 // nothing (same as alignment_top)
         }
@@ -291,7 +306,13 @@
             this.mMetadata = metadata;
         }
 
-        public boolean contains(float x, float y)  {
+        /**
+         * Returns true if x,y coordinate is within bounds
+         * @param x x-coordinate
+         * @param y y-coordinate
+         * @return x,y coordinate is within bounds
+         */
+        public boolean contains(float x, float y) {
             return x >= mLeft && x < mRight
                     && y >= mTop && y < mBottom;
         }
@@ -341,16 +362,22 @@
     public void initializeContext(RemoteContext context) {
         mRemoteComposeState.reset();
         mClickAreas.clear();
-
+        mRemoteComposeState.setNextId(RemoteComposeState.START_ID);
         context.mDocument = this;
         context.mRemoteComposeState = mRemoteComposeState;
-
         // mark context to be in DATA mode, which will skip the painting ops.
         context.mMode = RemoteContext.ContextMode.DATA;
-        for (Operation op: mOperations) {
+        mTimeVariables.updateTime(context);
+
+        for (Operation op : mOperations) {
+            if (op instanceof VariableSupport) {
+                ((VariableSupport) op).updateVariables(context);
+                ((VariableSupport) op).registerListening(context);
+            }
             op.apply(context);
         }
         context.mMode = RemoteContext.ContextMode.UNSET;
+
     }
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -375,7 +402,7 @@
      * @param minorVersion minor version number, increased when adding new features
      * @param patch        patch level, increased upon bugfixes
      */
-    void  setVersion(int majorVersion, int minorVersion, int patch) {
+    void setVersion(int majorVersion, int minorVersion, int patch) {
         mVersion = new Version(majorVersion, minorVersion, patch);
     }
 
@@ -389,13 +416,13 @@
      * the click coordinates will be the one reported; the order of addition of those click areas
      * is therefore meaningful.
      *
-     * @param id       the id of the area, which will be reported on click
+     * @param id                 the id of the area, which will be reported on click
      * @param contentDescription the content description (used for accessibility)
-     * @param left     the left coordinate of the click area (in pixels)
-     * @param top      the top coordinate of the click area (in pixels)
-     * @param right    the right coordinate of the click area (in pixels)
-     * @param bottom   the bottom coordinate of the click area (in pixels)
-     * @param metadata arbitrary metadata associated with the are, also reported on click
+     * @param left               the left coordinate of the click area (in pixels)
+     * @param top                the top coordinate of the click area (in pixels)
+     * @param right              the right coordinate of the click area (in pixels)
+     * @param bottom             the bottom coordinate of the click area (in pixels)
+     * @param metadata           arbitrary metadata associated with the are, also reported on click
      */
     public void addClickArea(int id, String contentDescription,
                              float left, float top, float right, float bottom, String metadata) {
@@ -417,7 +444,7 @@
      * listeners.
      */
     public void onClick(float x, float y) {
-        for (ClickAreaRepresentation clickArea: mClickAreas) {
+        for (ClickAreaRepresentation clickArea : mClickAreas) {
             if (clickArea.contains(x, y)) {
                 warnClickListeners(clickArea);
             }
@@ -430,7 +457,7 @@
      * @param id the click area id
      */
     public void performClick(int id) {
-        for (ClickAreaRepresentation clickArea: mClickAreas) {
+        for (ClickAreaRepresentation clickArea : mClickAreas) {
             if (clickArea.mId == id) {
                 warnClickListeners(clickArea);
             }
@@ -441,17 +468,36 @@
      * Warn click listeners when a click area is activated
      */
     private void warnClickListeners(ClickAreaRepresentation clickArea) {
-        for (ClickCallbacks listener: mClickListeners) {
+        for (ClickCallbacks listener : mClickListeners) {
             listener.click(clickArea.mId, clickArea.mMetadata);
         }
     }
 
-    ///////////////////////////////////////////////////////////////////////////////////////////////
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        for (Operation op : mOperations) {
+            builder.append(op.toString());
+            builder.append("\n");
+        }
+        return builder.toString();
+    }
+
+    //////////////////////////////////////////////////////////////////////////
     // Painting
-    ///////////////////////////////////////////////////////////////////////////////////////////////
+    //////////////////////////////////////////////////////////////////////////
 
     private final float[] mScaleOutput = new float[2];
     private final float[] mTranslateOutput = new float[2];
+    private int mRepaintNext = -1; // delay to next repaint -1 = don't 1 = asap
+
+    /**
+     * Returns > 0 if it needs to repaint
+     * @return
+     */
+    public int needsRepaint() {
+        return mRepaintNext;
+    }
 
     /**
      * Paint the document
@@ -475,6 +521,11 @@
             context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]);
             context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]);
         }
+        mTimeVariables.updateTime(context);
+        context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, getWidth());
+        context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, getHeight());
+        mRepaintNext = context.updateOps();
+
         for (Operation op : mOperations) {
             // operations will only be executed if no theme is set (ie UNSPECIFIED)
             // or the theme is equal as the one passed in argument to paint.
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 54b277a..4b45ab6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -19,6 +19,7 @@
 import com.android.internal.widget.remotecompose.core.operations.ClickArea;
 import com.android.internal.widget.remotecompose.core.operations.ClipPath;
 import com.android.internal.widget.remotecompose.core.operations.ClipRect;
+import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
 import com.android.internal.widget.remotecompose.core.operations.DrawArc;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
@@ -28,9 +29,12 @@
 import com.android.internal.widget.remotecompose.core.operations.DrawPath;
 import com.android.internal.widget.remotecompose.core.operations.DrawRect;
 import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawText;
+import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
 import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
-import com.android.internal.widget.remotecompose.core.operations.DrawTextRun;
 import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
+import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.Header;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
@@ -42,7 +46,10 @@
 import com.android.internal.widget.remotecompose.core.operations.PathData;
 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
 import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
 import com.android.internal.widget.remotecompose.core.operations.TextData;
+import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextMerge;
 import com.android.internal.widget.remotecompose.core.operations.Theme;
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
 
@@ -67,9 +74,10 @@
     public static final int DRAW_BITMAP = 44;
     public static final int DRAW_BITMAP_INT = 66;
     public static final int DATA_BITMAP = 101;
+    public static final int DATA_SHADER = 45;
     public static final int DATA_TEXT = 102;
 
-/////////////////////////////=====================
+    /////////////////////////////=====================
     public static final int CLIP_PATH = 38;
     public static final int CLIP_RECT = 39;
     public static final int PAINT_VALUES = 40;
@@ -91,6 +99,12 @@
     public static final int MATRIX_SAVE = 130;
     public static final int MATRIX_RESTORE = 131;
     public static final int MATRIX_SET = 132;
+    public static final int DATA_FLOAT = 80;
+    public static final int ANIMATED_FLOAT = 81;
+    public static final int DRAW_TEXT_ANCHOR = 133;
+    public static final int COLOR_EXPRESSIONS = 134;
+    public static final int TEXT_FROM_FLOAT = 135;
+    public static final int TEXT_MERGE = 136;
 
     /////////////////////////////////////////======================
     public static IntMap<CompanionOperation> map = new IntMap<>();
@@ -114,7 +128,7 @@
         map.put(DRAW_RECT, DrawRect.COMPANION);
         map.put(DRAW_ROUND_RECT, DrawRoundRect.COMPANION);
         map.put(DRAW_TEXT_ON_PATH, DrawTextOnPath.COMPANION);
-        map.put(DRAW_TEXT_RUN, DrawTextRun.COMPANION);
+        map.put(DRAW_TEXT_RUN, DrawText.COMPANION);
         map.put(DRAW_TWEEN_PATH, DrawTweenPath.COMPANION);
         map.put(DATA_PATH, PathData.COMPANION);
         map.put(PAINT_VALUES, PaintData.COMPANION);
@@ -126,6 +140,13 @@
         map.put(MATRIX_TRANSLATE, MatrixTranslate.COMPANION);
         map.put(CLIP_PATH, ClipPath.COMPANION);
         map.put(CLIP_RECT, ClipRect.COMPANION);
+        map.put(DATA_SHADER, ShaderData.COMPANION);
+        map.put(DATA_FLOAT, FloatConstant.COMPANION);
+        map.put(ANIMATED_FLOAT, FloatExpression.COMPANION);
+        map.put(DRAW_TEXT_ANCHOR, DrawTextAnchored.COMPANION);
+        map.put(COLOR_EXPRESSIONS, ColorExpression.COMPANION);
+        map.put(TEXT_FROM_FLOAT, TextFromFloat.COMPANION);
+        map.put(TEXT_MERGE, TextMerge.COMPANION);
 
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index eece8ad52..ecd0efc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -68,7 +68,35 @@
 
     public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset);
 
-    public abstract void drawTextRun(int textID,
+    /**
+     * Return the dimensions (left, top, right, bottom).
+     * Relative to a drawTextRun x=0, y=0;
+     *
+     * @param textId
+     * @param start
+     * @param end    if end is -1 it means the whole string
+     * @param monospace measure with better support for monospace
+     * @param bounds the bounds (left, top, right, bottom)
+     */
+    public abstract void getTextBounds(int textId,
+                                       int start,
+                                       int end,
+                                       boolean monospace,
+                                       float[]bounds);
+
+    /**
+     * Draw a text starting ast x,y
+     *
+     * @param textId reference to the text
+     * @param start
+     * @param end
+     * @param contextStart
+     * @param contextEnd
+     * @param x
+     * @param y
+     * @param rtl
+     */
+    public abstract void drawTextRun(int textId,
                                      int start,
                                      int end,
                                      int contextStart,
@@ -77,6 +105,14 @@
                                      float y,
                                      boolean rtl);
 
+    /**
+     * Draw an interpolation between two paths
+     * @param path1Id
+     * @param path2Id
+     * @param tween  0.0 = is path1 1.0 is path2
+     * @param start
+     * @param stop
+     */
     public abstract void drawTweenPath(int path1Id,
                                        int path2Id,
                                        float tween,
@@ -85,21 +121,70 @@
 
     public abstract void applyPaint(PaintBundle mPaintData);
 
-    public abstract void mtrixScale(float scaleX, float scaleY, float centerX, float centerY);
+    /**
+     * Scale the rendering by scaleX and saleY (1.0 = no scale).
+     * Scaling is done about centerX,centerY.
+     *
+     * @param scaleX
+     * @param scaleY
+     * @param centerX
+     * @param centerY
+     */
+    public abstract void matrixScale(float scaleX, float scaleY, float centerX, float centerY);
 
+    /**
+     * Translate the rendering
+     * @param translateX
+     * @param translateY
+     */
     public abstract void matrixTranslate(float translateX, float translateY);
 
+    /**
+     * Skew the rendering
+     * @param skewX
+     * @param skewY
+     */
     public abstract void matrixSkew(float skewX, float skewY);
 
+    /**
+     * Rotate the rendering.
+     * Note rotates are cumulative.
+     * @param rotate angle to rotate
+     * @param pivotX x-coordinate about which to rotate
+     * @param pivotY y-coordinate about which to rotate
+     */
     public abstract void matrixRotate(float rotate, float pivotX, float pivotY);
 
+    /**
+     * Save the current state of the transform
+     */
     public abstract void matrixSave();
 
+    /**
+     * Restore the previously saved state of the transform
+     */
     public abstract void matrixRestore();
 
+    /**
+     * Set the clip to a rectangle.
+     * Drawing outside the current clip region will have no effect
+     * @param left
+     * @param top
+     * @param right
+     * @param bottom
+     */
     public abstract void clipRect(float left, float top, float right, float bottom);
 
+    /**
+     * Clip based on a path.
+     * @param pathId
+     * @param regionOp
+     */
     public abstract void clipPath(int pathId, int regionOp);
 
+    /**
+     * Reset the paint
+     */
+    public abstract void reset();
 }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index c2e8131..52fc314 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -19,6 +19,7 @@
 import com.android.internal.widget.remotecompose.core.operations.ClickArea;
 import com.android.internal.widget.remotecompose.core.operations.ClipPath;
 import com.android.internal.widget.remotecompose.core.operations.ClipRect;
+import com.android.internal.widget.remotecompose.core.operations.ColorExpression;
 import com.android.internal.widget.remotecompose.core.operations.DrawArc;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
 import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
@@ -28,9 +29,12 @@
 import com.android.internal.widget.remotecompose.core.operations.DrawPath;
 import com.android.internal.widget.remotecompose.core.operations.DrawRect;
 import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawText;
+import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
 import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
-import com.android.internal.widget.remotecompose.core.operations.DrawTextRun;
 import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath;
+import com.android.internal.widget.remotecompose.core.operations.FloatConstant;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
 import com.android.internal.widget.remotecompose.core.operations.Header;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRestore;
 import com.android.internal.widget.remotecompose.core.operations.MatrixRotate;
@@ -43,8 +47,12 @@
 import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
 import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
 import com.android.internal.widget.remotecompose.core.operations.TextData;
+import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextMerge;
 import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
+import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -58,9 +66,20 @@
  * Provides an abstract buffer to encode/decode RemoteCompose operations
  */
 public class RemoteComposeBuffer {
+    public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD;
+    public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE;
+    public static final int EASING_CUBIC_DECELERATE = FloatAnimation.CUBIC_DECELERATE;
+    public static final int EASING_CUBIC_LINEAR = FloatAnimation.CUBIC_LINEAR;
+    public static final int EASING_CUBIC_ANTICIPATE = FloatAnimation.CUBIC_ANTICIPATE;
+    public static final int EASING_CUBIC_OVERSHOOT = FloatAnimation.CUBIC_OVERSHOOT;
+    public static final int EASING_CUBIC_CUSTOM = FloatAnimation.CUBIC_CUSTOM;
+    public static final int EASING_SPLINE_CUSTOM = FloatAnimation.SPLINE_CUSTOM;
+    public static final int EASING_EASE_OUT_BOUNCE = FloatAnimation.EASE_OUT_BOUNCE;
+    public static final int EASING_EASE_OUT_ELASTIC = FloatAnimation.EASE_OUT_ELASTIC;
     WireBuffer mBuffer = new WireBuffer();
     Platform mPlatform = null;
     RemoteComposeState mRemoteComposeState;
+    private static final boolean DEBUG = false;
 
     /**
      * Provides an abstract buffer to encode/decode RemoteCompose operations
@@ -171,7 +190,7 @@
      *
      * @param text the string to inject in the buffer
      */
-    int addText(String text) {
+    public int addText(String text) {
         int id = mRemoteComposeState.dataGetId(text);
         if (id == -1) {
             id = mRemoteComposeState.cache(text);
@@ -350,7 +369,6 @@
         addDrawPath(id);
     }
 
-
     /**
      * Draw the specified path
      *
@@ -426,12 +444,160 @@
                                float y,
                                boolean rtl) {
         int textId = addText(text);
-        DrawTextRun.COMPANION.apply(
+        DrawText.COMPANION.apply(
                 mBuffer, textId, start, end,
                 contextStart, contextEnd, x, y, rtl);
     }
 
     /**
+     * Draw the text, with origin at (x,y). The origin is interpreted
+     * based on the Align setting in the paint.
+     *
+     * @param textId       The text to be drawn
+     * @param start        The index of the first character in text to draw
+     * @param end          (end - 1) is the index of the last character in text to draw
+     * @param contextStart
+     * @param contextEnd
+     * @param x            The x-coordinate of the origin of the text being drawn
+     * @param y            The y-coordinate of the baseline of the text being drawn
+     * @param rtl          Draw RTTL
+     */
+    public void addDrawTextRun(int textId,
+                               int start,
+                               int end,
+                               int contextStart,
+                               int contextEnd,
+                               float x,
+                               float y,
+                               boolean rtl) {
+        DrawText.COMPANION.apply(
+                mBuffer, textId, start, end,
+                contextStart, contextEnd, x, y, rtl);
+    }
+
+    /**
+     * Draw a text on canvas at relative to position (x, y),
+     * offset panX and panY.
+     * <br>
+     * The panning factors (panX, panY)  mapped to the
+     * resulting bounding box of the text, in such a way that a
+     * panning factor of (0.0, 0.0) would center the text at (x, y)
+     * <ul>
+     * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
+     * <li>Panning of  1.0,  1.0 - the text is below and to the left</li>
+     * <li>Panning of  1.0,  0.0 - the test is centered & to the right of x,y</li>
+     * </ul>
+     * Setting panY to NaN results in y being the baseline of the text.
+     *
+     * @param text  text to draw
+     * @param x     Coordinate of the Anchor
+     * @param y     Coordinate of the Anchor
+     * @param panX  justifies text -1.0=right, 0.0=center, 1.0=left
+     * @param panY  position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+     * @param flags 1 = RTL
+     */
+    public void drawTextAnchored(String text,
+                                 float x,
+                                 float y,
+                                 float panX,
+                                 float panY,
+                                 int flags) {
+        int textId = addText(text);
+        DrawTextAnchored.COMPANION.apply(
+                mBuffer, textId,
+                x, y,
+                panX, panY,
+                flags);
+    }
+
+    /**
+     * Add a text and id so that it can be used
+     *
+     * @param text
+     * @return
+     */
+    public int createTextId(String text) {
+        return addText(text);
+    }
+
+    /**
+     * Merge two text (from id's) output one id
+     * @param id1 left id
+     * @param id2 right id
+     * @return new id that merges the two text
+     */
+    public int textMerge(int id1, int id2) {
+        int textId = addText(id1 + "+" + id2);
+        TextMerge.COMPANION.apply(mBuffer, textId, id1, id2);
+        return textId;
+    }
+
+    public static final int PAD_AFTER_SPACE = TextFromFloat.PAD_AFTER_SPACE;
+    public static final int PAD_AFTER_NONE = TextFromFloat.PAD_AFTER_NONE;
+    public static final int PAD_AFTER_ZERO = TextFromFloat.PAD_AFTER_ZERO;
+    public static final int PAD_PRE_SPACE = TextFromFloat.PAD_PRE_SPACE;
+    public static final int PAD_PRE_NONE = TextFromFloat.PAD_PRE_NONE;
+    public static final int PAD_PRE_ZERO = TextFromFloat.PAD_PRE_ZERO;
+
+    /**
+     * Create a TextFromFloat command which creates text from a Float.
+     *
+     * @param value        The value to convert
+     * @param digitsBefore the digits before the decimal point
+     * @param digitsAfter  the digits after the decimal point
+     * @param flags        configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
+     * @return id of the string that can be passed to drawTextAnchored
+     */
+    public int createTextFromFloat(float value, short digitsBefore,
+                                   short digitsAfter, int flags) {
+        String placeHolder = Utils.floatToString(value)
+                + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")";
+        int id = mRemoteComposeState.dataGetId(placeHolder);
+        if (id == -1) {
+            id = mRemoteComposeState.cache(placeHolder);
+            //   TextData.COMPANION.apply(mBuffer, id, text);
+        }
+        TextFromFloat.COMPANION.apply(mBuffer, id, value, digitsBefore,
+                digitsAfter, flags);
+        return id;
+    }
+
+    /**
+     * Draw a text on canvas at relative to position (x, y),
+     * offset panX and panY.
+     * <br>
+     * The panning factors (panX, panY)  mapped to the
+     * resulting bounding box of the text, in such a way that a
+     * panning factor of (0.0, 0.0) would center the text at (x, y)
+     * <ul>
+     * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
+     * <li>Panning of  1.0,  1.0 - the text is below and to the left</li>
+     * <li>Panning of  1.0,  0.0 - the test is centered & to the right of x,y</li>
+     * </ul>
+     * Setting panY to NaN results in y being the baseline of the text.
+     *
+     * @param textId text to draw
+     * @param x      Coordinate of the Anchor
+     * @param y      Coordinate of the Anchor
+     * @param panX   justifies text -1.0=right, 0.0=center, 1.0=left
+     * @param panY   position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+     * @param flags  1 = RTL
+     */
+    public void drawTextAnchored(int textId,
+                                 float x,
+                                 float y,
+                                 float panX,
+                                 float panY,
+                                 int flags) {
+
+        DrawTextAnchored.COMPANION.apply(
+                mBuffer, textId,
+                x, y,
+                panX, panY,
+                flags);
+    }
+
+    /**
      * draw an interpolation between two paths that have the same pattern
      * <p>
      * Warning paths objects are not immutable and this is not taken into consideration
@@ -490,6 +656,10 @@
         return id;
     }
 
+    /**
+     * Adds a paint Bundle to the doc
+     * @param paint
+     */
     public void addPaint(PaintBundle paint) {
         PaintData.COMPANION.apply(mBuffer, paint);
     }
@@ -499,7 +669,9 @@
         mBuffer.setIndex(0);
         while (mBuffer.available()) {
             int opId = mBuffer.readByte();
-            System.out.println(">>> " + opId);
+            if (DEBUG) {
+                Utils.log(">> " + opId);
+            }
             CompanionOperation operation = Operations.map.get(opId);
             if (operation == null) {
                 throw new RuntimeException("Unknown operation encountered " + opId);
@@ -519,7 +691,6 @@
         Theme.COMPANION.apply(mBuffer, theme);
     }
 
-
     static String version() {
         return "v1.0";
     }
@@ -654,8 +825,8 @@
     /**
      * Add a pre-concat of the current matrix with the specified scale.
      *
-     * @param scaleX  The amount to scale in X
-     * @param scaleY  The amount to scale in Y
+     * @param scaleX The amount to scale in X
+     * @param scaleY The amount to scale in Y
      */
     public void addMatrixScale(float scaleX, float scaleY) {
         MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, Float.NaN, Float.NaN);
@@ -673,12 +844,174 @@
         MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, centerX, centerY);
     }
 
+    /**
+     * sets the clip based on clip id
+     * @param pathId 0 clears the clip
+     */
     public void addClipPath(int pathId) {
         ClipPath.COMPANION.apply(mBuffer, pathId);
     }
 
+    /**
+     * Sets the clip based on clip rec
+     * @param left
+     * @param top
+     * @param right
+     * @param bottom
+     */
     public void addClipRect(float left, float top, float right, float bottom) {
         ClipRect.COMPANION.apply(mBuffer, left, top, right, bottom);
     }
+
+    /**
+     * Add a float return a NaN number pointing to that float
+     * @param value
+     * @return
+     */
+    public float addFloat(float value) {
+        int id = mRemoteComposeState.cacheFloat(value);
+        FloatConstant.COMPANION.apply(mBuffer, id, value);
+        return Utils.asNan(id);
+    }
+
+    /**
+     * Add a float that is a computation based on variables
+     * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
+     * @return NaN id of the result of the calculation
+     */
+    public float addAnimatedFloat(float... value) {
+        int id = mRemoteComposeState.cache(value);
+        FloatExpression.COMPANION.apply(mBuffer, id, value, null);
+        return Utils.asNan(id);
+    }
+
+    /**
+     * Add a float that is a computation based on variables.
+     * see packAnimation
+     * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
+     * @param animation Array of floats that represents animation
+     * @return NaN id of the result of the calculation
+     */
+    public float addAnimatedFloat(float[] value, float[] animation) {
+        int id = mRemoteComposeState.cache(value);
+        FloatExpression.COMPANION.apply(mBuffer, id, value, animation);
+        return Utils.asNan(id);
+    }
+
+    /**
+     * Add a color that represents the tween between two colors
+     * @param color1
+     * @param color2
+     * @param tween
+     * @return id of the color (color ids are short)
+     */
+    public short addColorExpression(int color1, int color2, float tween) {
+        ColorExpression c = new ColorExpression(0, 0, color1, color2, tween);
+        short id = (short) mRemoteComposeState.cache(c);
+        c.mId = id;
+        c.write(mBuffer);
+        return id;
+    }
+
+    /**
+     * Add a color that represents the tween between two colors where color1
+     * is the id of a color
+     * @param color1
+     * @param color2
+     * @param tween
+     * @return id of the color (color ids are short)
+     */
+    public short addColorExpression(short color1, int color2, float tween) {
+        ColorExpression c = new ColorExpression(0, 1, color1, color2, tween);
+        short id = (short) mRemoteComposeState.cache(c);
+        c.mId = id;
+        c.write(mBuffer);
+        return id;
+    }
+
+    /**
+     * Add a color that represents the tween between two colors where color2
+     * is the id of a color
+     * @param color1
+     * @param color2
+     * @param tween
+     * @return id of the color (color ids are short)
+     */
+    public short addColorExpression(int color1, short color2, float tween) {
+        ColorExpression c = new ColorExpression(0, 2, color1, color2, tween);
+        short id = (short) mRemoteComposeState.cache(c);
+        c.mId = id;
+        c.write(mBuffer);
+        return id;
+    }
+
+    /**
+     * Add a color that represents the tween between two colors where color1 &
+     * color2 are the ids of colors
+     * @param color1
+     * @param color2
+     * @param tween
+     * @return id of the color (color ids are short)
+     */
+    public short addColorExpression(short color1, short color2, float tween) {
+        ColorExpression c = new ColorExpression(0, 3, color1, color2, tween);
+        short id = (short) mRemoteComposeState.cache(c);
+        c.mId = id;
+        c.write(mBuffer);
+        return id;
+    }
+
+    /**
+     *  Color calculated by Hue saturation and value.
+     *  (as floats they can be variables used to create color transitions)
+     * @param hue
+     * @param sat
+     * @param value
+     * @return id of the color (color ids are short)
+     */
+    public short addColorExpression(float hue, float sat, float value) {
+        ColorExpression c = new ColorExpression(0, hue, sat, value);
+        short id = (short) mRemoteComposeState.cache(c);
+        c.mId = id;
+        c.write(mBuffer);
+        return id;
+    }
+
+    /**
+     * Color calculated by Alpha, Hue saturation and value.
+     * (as floats they can be variables used to create color transitions)
+     * @param alpha
+     * @param hue
+     * @param sat
+     * @param value
+     * @return id of the color (color ids are short)
+     */
+    public short addColorExpression(int alpha, float hue, float sat, float value) {
+        ColorExpression c = new ColorExpression(0, alpha, hue, sat, value);
+        short id = (short) mRemoteComposeState.cache(c);
+        c.mId = id;
+        c.write(mBuffer);
+        return id;
+    }
+
+    /**
+     * create and animation based on description and return as an array of
+     * floats. see addAnimatedFloat
+     * @param duration
+     * @param type
+     * @param spec
+     * @param initialValue
+     * @param wrap
+     * @return
+     */
+    public static float[] packAnimation(float duration,
+                                        int type,
+                                        float[] spec,
+                                        float initialValue,
+                                        float wrap) {
+
+        return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap);
+    }
+
 }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 17e8c83..66a37e67 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -15,26 +15,53 @@
  */
 package com.android.internal.widget.remotecompose.core;
 
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT;
+import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH;
+
 import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 
 /**
  * Represents runtime state for a RemoteCompose document
+ * State includes things like the value of variables
  */
 public class RemoteComposeState {
-
+    public static final int START_ID = 42;
+    private static final int MAX_FLOATS = 500;
     private final IntMap<Object> mIntDataMap = new IntMap<>();
     private final IntMap<Boolean> mIntWrittenMap = new IntMap<>();
     private final HashMap<Object, Integer> mDataIntMap = new HashMap();
+    private final float[] mFloatMap = new float[MAX_FLOATS]; // efficient cache
+    private final int[] mColorMap = new int[MAX_FLOATS]; // efficient cache
+    private int mNextId = START_ID;
 
-    private static int sNextId = 42;
+    {
+        for (int i = 0; i < mFloatMap.length; i++) {
+            mFloatMap[i] = Float.NaN;
+        }
+    }
 
-    public Object getFromId(int id)  {
+    /**
+     * Get Object based on id. The system will cache things like bitmaps
+     * Paths etc. They can be accessed with this command
+     * @param id
+     * @return
+     */
+    public Object getFromId(int id) {
         return mIntDataMap.get(id);
     }
 
-    public boolean containsId(int id)  {
+    /**
+     * true if the cache contain this id
+     * @param id
+     * @return
+     */
+    public boolean containsId(int id) {
         return mIntDataMap.get(id) != null;
     }
 
@@ -69,6 +96,65 @@
     }
 
     /**
+     * Insert an item in the cache
+     */
+    public void update(int id, Object item) {
+        mDataIntMap.remove(mIntDataMap.get(id));
+        mDataIntMap.put(item, id);
+        mIntDataMap.put(id, item);
+    }
+
+    /**
+     * Insert an item in the cache
+     */
+    public int cacheFloat(float item) {
+        int id = nextId();
+        mFloatMap[id] = item;
+        return id;
+    }
+
+    /**
+     * Insert an item in the cache
+     */
+    public void cacheFloat(int id, float item) {
+        mFloatMap[id] = item;
+    }
+
+    /**
+     * Insert an item in the cache
+     */
+    public void updateFloat(int id, float item) {
+        mFloatMap[id] = item;
+    }
+
+    /**
+     * get float
+     */
+    public float getFloat(int id) {
+        return mFloatMap[id];
+    }
+
+    /**
+     * Get the float value
+     *
+     * @param id
+     * @return
+     */
+    public int getColor(int id) {
+        return mColorMap[id];
+    }
+
+    /**
+     * Modify the color at id.
+     * @param id
+     * @param color
+     */
+    public void updateColor(int id, int color) {
+        mColorMap[id] = color;
+    }
+
+
+    /**
      * Method to determine if a cached value has been written to the documents WireBuffer based on
      * its id.
      */
@@ -79,22 +165,90 @@
     /**
      * Method to mark that a value, represented by its id, has been written to the WireBuffer
      */
-    public void  markWritten(int id) {
+    public void markWritten(int id) {
         mIntWrittenMap.put(id, true);
     }
 
     /**
-     *  Clear the record of the values that have been written to the WireBuffer.
+     * Clear the record of the values that have been written to the WireBuffer.
      */
     void reset() {
         mIntWrittenMap.clear();
     }
 
-    public static int nextId() {
-        return sNextId++;
+    /**
+     * Get the next available id
+     * @return
+     */
+    public int nextId() {
+        return mNextId++;
     }
-    public static void setNextId(int id) {
-        sNextId = id;
+
+    /**
+     * Set the next id
+     * @param id
+     */
+    public void setNextId(int id) {
+        mNextId = id;
+    }
+
+    IntMap<ArrayList<VariableSupport>> mVarListeners = new IntMap<>();
+    ArrayList<VariableSupport> mAllVarListeners = new ArrayList<>();
+
+    private void add(int id, VariableSupport variableSupport) {
+        ArrayList<VariableSupport> v = mVarListeners.get(id);
+        if (v == null) {
+            v = new ArrayList<VariableSupport>();
+            mVarListeners.put(id, v);
+        }
+        v.add(variableSupport);
+        mAllVarListeners.add(variableSupport);
+    }
+
+    /**
+     * Commands that listen to variables add themselves.
+     * @param id
+     * @param variableSupport
+     */
+    public void listenToVar(int id, VariableSupport variableSupport) {
+        add(id, variableSupport);
+    }
+
+    /**
+     * List of Commands that need to be updated
+     * @param context
+     * @return
+     */
+    public int getOpsToUpdate(RemoteContext context) {
+        for (VariableSupport vs : mAllVarListeners) {
+            vs.updateVariables(context);
+        }
+        if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) {
+            return 1;
+        }
+        if (mVarListeners.get(ID_TIME_IN_SEC) != null) {
+            return 1000;
+        }
+        if (mVarListeners.get(ID_TIME_IN_MIN) != null) {
+            return 1000 * 60;
+        }
+        return -1;
+    }
+
+    /**
+     * Set the width of the overall document on screen.
+     * @param width
+     */
+    public void setWindowWidth(float width) {
+        updateFloat(ID_WINDOW_WIDTH, width);
+    }
+
+    /**
+     * Set the width of the overall document on screen.
+     * @param height
+     */
+    public void setWindowHeight(float height) {
+        updateFloat(ID_WINDOW_HEIGHT, height);
     }
 
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index d16cbc5..7e72168 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.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.
@@ -15,7 +15,10 @@
  */
 package com.android.internal.widget.remotecompose.core;
 
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
 import com.android.internal.widget.remotecompose.core.operations.Theme;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
 
 /**
  * Specify an abstract context used to playback RemoteCompose documents
@@ -27,7 +30,7 @@
 public abstract class RemoteContext {
     protected CoreDocument mDocument;
     public RemoteComposeState mRemoteComposeState;
-
+    long mStart = System.nanoTime(); // todo This should be set at a hi level
     protected PaintContext mPaintContext = null;
     ContextMode mMode = ContextMode.UNSET;
 
@@ -37,9 +40,40 @@
     public float mWidth = 0f;
     public float mHeight = 0f;
 
+    /**
+     * Load a path under an id.
+     * Paths can be use in clip drawPath and drawTweenPath
+     * @param instanceId
+     * @param floatPath
+     */
     public abstract void loadPathData(int instanceId, float[] floatPath);
 
     /**
+     * Associate a name with a give id.
+     *
+     * @param varName
+     * @param varId
+     * @param varType
+     */
+    public abstract void loadVariableName(String varName, int varId, int varType);
+
+    /**
+     * Save a color under a given id
+     * @param id
+     * @param color
+     */
+    public abstract void loadColor(int id, int color);
+
+    /**
+     * gets the time animation clock as float in seconds
+     * @return a monotonic time in seconds (arbitrary zero point)
+     */
+    public float getAnimationTime() {
+        return (System.nanoTime() - mStart) * 1E-9f;
+    }
+
+
+    /**
      * The context can be used in a few different mode, allowing operations to skip being executed:
      * - UNSET : all operations will get executed
      * - DATA : only operations dealing with DATA (eg loading a bitmap) should execute
@@ -96,6 +130,8 @@
     public void header(int majorVersion, int minorVersion, int patchVersion,
                        int width, int height, long capabilities
     ) {
+        mRemoteComposeState.setWindowWidth(width);
+        mRemoteComposeState.setWindowHeight(height);
         mDocument.setVersion(majorVersion, minorVersion, patchVersion);
         mDocument.setWidth(width);
         mDocument.setHeight(height);
@@ -137,9 +173,105 @@
     ///////////////////////////////////////////////////////////////////////////////////////////////
     // Data handling
     ///////////////////////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Save a bitmap under an imageId
+     * @param imageId
+     * @param width
+     * @param height
+     * @param bitmap
+     */
     public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap);
+
+    /**
+     * Save a string under a given id
+     * @param id
+     * @param text
+     */
     public abstract void loadText(int id, String text);
 
+    /**
+     * Get a string given an id
+     * @param id
+     * @return
+     */
+    public abstract String getText(int id);
+
+    /**
+     * Load a float
+     * @param id
+     * @param value
+     */
+    public abstract void loadFloat(int id, float value);
+
+    /**
+     * Load an animated float associated with an id
+     * Todo: Remove?
+     * @param id
+     * @param animatedFloat
+     */
+    public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat);
+
+    /**
+     * Save a shader under and ID
+     * @param id
+     * @param value
+     */
+    public abstract void loadShader(int id, ShaderData value);
+
+    /**
+     * Get a float given an id
+     * @param id
+     * @return
+     */
+    public abstract float getFloat(int id);
+
+    /**
+     * Get the color given and ID
+     * @param id
+     * @return
+     */
+    public abstract int getColor(int id);
+
+    /**
+     * called to notify system that a command is interested in a variable
+     * @param id
+     * @param variableSupport
+     */
+    public abstract void listensTo(int id, VariableSupport variableSupport);
+
+    /**
+     * Notify commands with variables have changed
+     * @return
+     */
+    public abstract int updateOps();
+
+    /**
+     * Get a shader given the id
+     * @param id
+     * @return
+     */
+    public abstract ShaderData getShader(int id);
+
+    public static final int ID_CONTINUOUS_SEC = 1;
+    public static final int ID_TIME_IN_SEC = 2;
+    public static final int ID_TIME_IN_MIN = 3;
+    public static final int ID_TIME_IN_HR = 4;
+    public static final int ID_WINDOW_WIDTH = 5;
+    public static final int ID_WINDOW_HEIGHT = 6;
+    public static final int ID_COMPONENT_WIDTH = 7;
+    public static final int ID_COMPONENT_HEIGHT = 8;
+    public static final int ID_CALENDAR_MONTH = 9;
+
+    public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC);
+    public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC);
+    public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN);
+    public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR);
+    public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH);
+    public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH);
+    public static final float FLOAT_WINDOW_HEIGHT = Utils.asNan(ID_WINDOW_HEIGHT);
+    public static final float FLOAT_COMPONENT_WIDTH = Utils.asNan(ID_COMPONENT_WIDTH);
+    public static final float FLOAT_COMPONENT_HEIGHT = Utils.asNan(ID_COMPONENT_HEIGHT);
     ///////////////////////////////////////////////////////////////////////////////////////////////
     // Click handling
     ///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
new file mode 100644
index 0000000..e9708b7
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -0,0 +1,51 @@
+/*
+ * 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.internal.widget.remotecompose.core;
+
+import java.time.LocalDateTime;
+
+/**
+ * This generates the standard system variables for time.
+ */
+public class TimeVariables {
+    /**
+     * This class populates all time variables in the system
+     * @param context
+     */
+    public void updateTime(RemoteContext context) {
+        LocalDateTime dateTime = LocalDateTime.now();
+        // This define the time in the format
+        // seconds run from Midnight=0 quantized to seconds hour 0..3599
+        // minutes run from Midnight=0 quantized to minutes 0..1439
+        // hours run from Midnight=0 quantized to Hours 0-23
+        // CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600
+        // CONTINUOUS_SEC is accurate to milliseconds due to float precession
+        int month = dateTime.getDayOfMonth();
+        int hour = dateTime.getHour();
+        int minute = dateTime.getMinute();
+        int seconds = dateTime.getSecond();
+        int currentMinute = hour * 60 + minute;
+        int currentSeconds = minute * 60 + seconds;
+        float sec = currentSeconds + dateTime.getNano() * 1E-9f;
+
+        context.loadFloat(RemoteContext.ID_CONTINUOUS_SEC, sec);
+        context.loadFloat(RemoteContext.ID_TIME_IN_SEC, currentSeconds);
+        context.loadFloat(RemoteContext.ID_TIME_IN_MIN, currentMinute);
+        context.loadFloat(RemoteContext.ID_TIME_IN_HR, hour);
+        context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
+
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
new file mode 100644
index 0000000..d59b1bc
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core;
+
+/**
+ * Interface for operators that interact with variables
+ * Threw this they register to listen to particular variables
+ * and are notified when they change
+ */
+public interface VariableSupport {
+    /**
+     * Call to allow an operator to register interest in variables.
+     * Typically they call context.listensTo(id, this)
+     * @param context
+     */
+    void registerListening(RemoteContext context);
+
+    /**
+     * Called to be notified that the variables you are interested have changed.
+     * @param context
+     */
+    void updateVariables(RemoteContext context);
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 76b7144..f186322 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -51,11 +51,12 @@
 
     @Override
     public String toString() {
-        return "BITMAP DATA $imageId";
+        return "BITMAP DATA " + mImageId;
     }
 
     public static class Companion implements CompanionOperation {
-        private Companion() {}
+        private Companion() {
+        }
 
         @Override
         public String name() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index 8d4a787..e6d5fe7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -24,6 +24,11 @@
 
 import java.util.List;
 
+/**
+ * Defines a path that clips a the subsequent drawing commands
+ * Use MatrixSave and MatrixRestore commands to remove clip
+ * TODO allow id 0 to mean null?
+ */
 public class ClipPath extends PaintOperation {
     public static final Companion COMPANION = new Companion();
     int mId;
@@ -93,5 +98,4 @@
     public void paint(PaintContext context) {
         context.clipPath(mId, mRegionOp);
     }
-}
-
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
index 803618a..613eceb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
@@ -15,88 +15,36 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
-
-public class ClipRect extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mLeft;
-    float mTop;
-    float mRight;
-    float mBottom;
+/**
+ * Support clip with a rectangle
+ */
+public class ClipRect extends DrawBase4 {
+    public static final Companion COMPANION =
+            new Companion(Operations.CLIP_RECT) {
+                @Override
+                public Operation construct(float x1,
+                                           float y1,
+                                           float x2,
+                                           float y2) {
+                    return new ClipRect(x1, y1, x2, y2);
+                }
+            };
 
     public ClipRect(
             float left,
             float top,
             float right,
             float bottom) {
-        mLeft = left;
-        mTop = top;
-        mRight = right;
-        mBottom = bottom;
-
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
-    }
-
-    @Override
-    public String toString() {
-        return "ClipRect " + mLeft + " " + mTop
-                + " " + mRight + " " + mBottom + ";";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float sLeft = buffer.readFloat();
-            float srcTop = buffer.readFloat();
-            float srcRight = buffer.readFloat();
-            float srcBottom = buffer.readFloat();
-
-            ClipRect op = new ClipRect(sLeft, srcTop, srcRight, srcBottom);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "ClipRect";
-        }
-
-        @Override
-        public int id() {
-            return Operations.CLIP_RECT;
-        }
-
-        public void apply(WireBuffer buffer,
-                          float left,
-                          float top,
-                          float right,
-                          float bottom) {
-            buffer.start(Operations.CLIP_RECT);
-            buffer.writeFloat(left);
-            buffer.writeFloat(top);
-            buffer.writeFloat(right);
-            buffer.writeFloat(bottom);
-        }
+        super(left, top, right, bottom);
+        mName = "ClipRect";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.clipRect(mLeft,
-                mTop,
-                mRight,
-                mBottom);
+        context.clipRect(mX1, mY1, mX2, mY2);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
new file mode 100644
index 0000000..7d28cea
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -0,0 +1,242 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to Colors
+ * Color modes
+ * mMode = 0 two colors and a tween
+ * mMode = 1 color1 is a colorID.
+ * mMode = 2 color2 is a colorID.
+ * mMode = 3 color1 & color2 are ids
+ * mMode = 4  H S V mode
+ */
+public class ColorExpression implements Operation, VariableSupport {
+    public int mId;
+    int mMode;
+    public int mColor1;
+    public int mColor2;
+    public float mTween = 0.0f;
+
+
+    public float mHue = 0; // only in Mode 4
+    public float mSat = 0;
+    public float mValue = 0;
+    public float mOutHue = 0; // only in Mode 4
+    public float mOutSat = 0;
+    public float mOutValue = 0;
+    public int mAlpha = 0xFF; // only used in hsv mode
+
+    public float mOutTween = 0.0f;
+    public int mOutColor1;
+    public int mOutColor2;
+    public static final Companion COMPANION = new Companion();
+    public static final int HSV_MODE = 4;
+    public ColorExpression(int id, float hue, float sat, float value) {
+        mMode = HSV_MODE;
+        mAlpha = 0xFF;
+        mOutHue = mHue = hue;
+        mOutSat = mSat = sat;
+        mOutValue = mValue = value;
+        mColor1 = Float.floatToRawIntBits(hue);
+        mColor2 = Float.floatToRawIntBits(sat);
+        mTween = value;
+    }
+    public ColorExpression(int id, int alpha, float hue, float sat, float value) {
+        mMode = HSV_MODE;
+        mAlpha = alpha;
+        mOutHue = mHue = hue;
+        mOutSat = mSat = sat;
+        mOutValue = mValue = value;
+        mColor1 = Float.floatToRawIntBits(hue);
+        mColor2 = Float.floatToRawIntBits(sat);
+        mTween = value;
+    }
+
+    public ColorExpression(int id, int mode, int color1, int color2, float tween) {
+        this.mId = id;
+        this.mMode = mode & 0xFF;
+        this.mAlpha = (mode >> 16) & 0xFF;
+        if (mMode == HSV_MODE) {
+            mOutHue = mHue = Float.intBitsToFloat(color1);
+            mOutSat = mSat = Float.intBitsToFloat(color2);
+            mOutValue = mValue = tween;
+        }
+        this.mColor1 = color1;
+        this.mColor2 = color2;
+        this.mTween = tween;
+        this.mOutTween = tween;
+        this.mOutColor1 = color1;
+        this.mOutColor2 = color2;
+
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        if (mMode == 4) {
+            if (Float.isNaN(mHue)) {
+                mOutHue = context.getFloat(Utils.idFromNan(mHue));
+            }
+            if (Float.isNaN(mSat)) {
+                mOutSat = context.getFloat(Utils.idFromNan(mSat));
+            }
+            if (Float.isNaN(mValue)) {
+                mOutValue = context.getFloat(Utils.idFromNan(mValue));
+            }
+        }
+        if (Float.isNaN(mTween)) {
+            mOutTween = context.getFloat(Utils.idFromNan(mTween));
+        }
+        if ((mMode & 1) == 1) {
+            mOutColor1 = context.getColor(mColor1);
+        }
+        if ((mMode & 2) == 2) {
+            mOutColor2 = context.getColor(mColor2);
+        }
+    }
+
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (mMode == 4) {
+            if (Float.isNaN(mHue)) {
+                context.listensTo(Utils.idFromNan(mHue), this);
+            }
+            if (Float.isNaN(mSat)) {
+                context.listensTo(Utils.idFromNan(mSat), this);
+            }
+            if (Float.isNaN(mValue)) {
+                context.listensTo(Utils.idFromNan(mValue), this);
+            }
+            return;
+        }
+        if (Float.isNaN(mTween)) {
+            context.listensTo(Utils.idFromNan(mTween), this);
+        }
+        if ((mMode & 1) == 1) {
+            context.listensTo(mColor1, this);
+        }
+        if ((mMode & 2) == 2) {
+            context.listensTo(mColor2, this);
+        }
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        if (mMode == 4) {
+            context.loadColor(mId, (mAlpha << 24)
+                    | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
+            return;
+        }
+        if (mOutTween == 0.0) {
+            context.loadColor(mId, mColor1);
+        } else {
+            if ((mMode & 1) == 1) {
+                mOutColor1 = context.getColor(mColor1);
+            }
+            if ((mMode & 2) == 2) {
+                mOutColor2 = context.getColor(mColor2);
+            }
+
+            context.loadColor(mId,
+                    Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
+        }
+
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        int mode = mMode | (mAlpha << 16);
+        COMPANION.apply(buffer, mId, mode, mColor1, mColor2, mTween);
+    }
+
+    @Override
+    public String toString() {
+        if (mMode == 4) {
+            return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue)
+                    + ", " + Utils.floatToString(mSat)
+                    + ", " + Utils.floatToString(mValue) + ")";
+        }
+
+        String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
+        String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
+        return "ColorExpression[" + mId + "] = tween(" + c1
+                + ", " + c2 + ", "
+                + Utils.floatToString(mTween) + ")";
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "ColorExpression";
+        }
+
+        @Override
+        public int id() {
+            return Operations.COLOR_EXPRESSIONS;
+        }
+
+        /**
+         * Call to write a ColorExpression object on the buffer
+         * @param buffer
+         * @param id of the ColorExpression object
+         * @param mode if colors are id or actual values
+         * @param color1
+         * @param color2
+         * @param tween
+         */
+        public void apply(WireBuffer buffer,
+                          int id, int mode,
+                          int color1, int color2, float tween) {
+            buffer.start(Operations.COLOR_EXPRESSIONS);
+            buffer.writeInt(id);
+            buffer.writeInt(mode);
+            buffer.writeInt(color1);
+            buffer.writeInt(color2);
+            buffer.writeFloat(tween);
+
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int id = buffer.readInt();
+            int mode = buffer.readInt();
+            int color1 = buffer.readInt();
+            int color2 = buffer.readInt();
+            float tween = buffer.readFloat();
+
+            operations.add(new ColorExpression(id, mode, color1, color2, tween));
+        }
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
index e829975..c176864 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
@@ -15,107 +15,36 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
+public class DrawArc extends DrawBase6 {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_ARC) {
+                @Override
+                public Operation construct(float v1,
+                                           float v2,
+                                           float v3,
+                                           float v4,
+                                           float v5,
+                                           float v6) {
+                    return new DrawArc(v1, v2, v3, v4, v5, v6);
+                }
+            };
 
-public class DrawArc extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mLeft;
-    float mTop;
-    float mRight;
-    float mBottom;
-    float mStartAngle;
-    float mSweepAngle;
-
-    public DrawArc(
-            float left,
-            float top,
-            float right,
-            float bottom,
-            float startAngle,
-            float sweepAngle) {
-        mLeft = left;
-        mTop = top;
-        mRight = right;
-        mBottom = bottom;
-        mStartAngle = startAngle;
-        mSweepAngle = sweepAngle;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mLeft,
-                mTop,
-                mRight,
-                mBottom,
-                mStartAngle,
-                mSweepAngle);
-    }
-
-    @Override
-    public String toString() {
-        return "DrawArc " + mLeft + " " + mTop
-                + " " + mRight + " " + mBottom + " "
-                + "- " + mStartAngle + " " + mSweepAngle + ";";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float sLeft = buffer.readFloat();
-            float srcTop = buffer.readFloat();
-            float srcRight = buffer.readFloat();
-            float srcBottom = buffer.readFloat();
-            float mStartAngle = buffer.readFloat();
-            float mSweepAngle = buffer.readFloat();
-            DrawArc op = new DrawArc(sLeft, srcTop, srcRight, srcBottom,
-                    mStartAngle, mSweepAngle);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "DrawArc";
-        }
-
-        @Override
-        public int id() {
-            return Operations.DRAW_ARC;
-        }
-
-        public void apply(WireBuffer buffer,
-                          float left,
-                          float top,
-                          float right,
-                          float bottom,
-                          float startAngle,
-                          float sweepAngle) {
-            buffer.start(Operations.DRAW_ARC);
-            buffer.writeFloat(left);
-            buffer.writeFloat(top);
-            buffer.writeFloat(right);
-            buffer.writeFloat(bottom);
-            buffer.writeFloat(startAngle);
-            buffer.writeFloat(sweepAngle);
-        }
+    public DrawArc(float v1,
+                   float v2,
+                   float v3,
+                   float v4,
+                   float v5,
+                   float v6) {
+        super(v1, v2, v3, v4, v5, v6);
+        mName = "DrawArc";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.drawArc(mLeft,
-                mTop,
-                mRight,
-                mBottom,
-                mStartAngle,
-                mSweepAngle);
+        context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
new file mode 100644
index 0000000..0963c13
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for commands that take 3 float
+ */
+public abstract class DrawBase2 extends PaintOperation
+        implements VariableSupport {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_CIRCLE) {
+                @Override
+                public Operation construct(float x1, float y1) {
+                    // subclass should return new DrawX(x1, y1);
+                    return null;
+                }
+            };
+    protected String mName = "DrawRectBase";
+    float mV1;
+    float mV2;
+    float mValue1;
+    float mValue2;
+
+    public DrawBase2(float v1, float v2) {
+        mValue1 = v1;
+        mValue2 = v2;
+        mV1 = v1;
+        mV2 = v2;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        mV1 = (Float.isNaN(mValue1))
+                ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+        mV2 = (Float.isNaN(mValue2))
+                ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mValue1)) {
+            context.listensTo(Utils.idFromNan(mValue1), this);
+        }
+        if (Float.isNaN(mValue2)) {
+            context.listensTo(Utils.idFromNan(mValue2), this);
+        }
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mV1, mV2);
+    }
+
+    @Override
+    public String toString() {
+        return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
+    }
+
+    public static class Companion implements CompanionOperation {
+        public final int OP_CODE;
+
+        protected Companion(int code) {
+            OP_CODE = code;
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            float v1 = buffer.readFloat();
+            float v2 = buffer.readFloat();
+
+            Operation op = construct(v1, v2);
+            operations.add(op);
+        }
+
+        /**
+         * Override to construct a 2 float value operation
+         * @param x1
+         * @param y1
+         * @return
+         */
+        public Operation construct(float x1, float y1) {
+            return null;
+        }
+
+        @Override
+        public String name() {
+            return "DrawRect";
+        }
+
+        @Override
+        public int id() {
+            return OP_CODE;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param x1
+         * @param y1
+         */
+        public void apply(WireBuffer buffer,
+                          float x1,
+                          float y1) {
+            buffer.start(OP_CODE);
+            buffer.writeFloat(x1);
+            buffer.writeFloat(y1);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
new file mode 100644
index 0000000..56b2f1f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for commands that take 3 float
+ */
+public abstract class DrawBase3 extends PaintOperation
+        implements VariableSupport {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_CIRCLE) {
+                @Override
+                public Operation construct(float x1, float y1, float x2) {
+                    // subclass should return new DrawX(x1, y1, x2, y2);
+                    return null;
+                }
+            };
+    protected String mName = "DrawRectBase";
+    float mV1;
+    float mV2;
+    float mV3;
+    float mValue1;
+    float mValue2;
+    float mValue3;
+
+    public DrawBase3(
+            float v1,
+            float v2,
+            float v3) {
+        mValue1 = v1;
+        mValue2 = v2;
+        mValue3 = v3;
+
+        mV1 = v1;
+        mV2 = v2;
+        mV3 = v3;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        mV1 = (Float.isNaN(mValue1))
+                ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+        mV2 = (Float.isNaN(mValue2))
+                ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+        mV3 = (Float.isNaN(mValue3))
+                ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mValue1)) {
+            context.listensTo(Utils.idFromNan(mValue1), this);
+        }
+        if (Float.isNaN(mValue2)) {
+            context.listensTo(Utils.idFromNan(mValue2), this);
+        }
+        if (Float.isNaN(mValue3)) {
+            context.listensTo(Utils.idFromNan(mValue3), this);
+        }
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mV1, mV2, mV3);
+    }
+
+    @Override
+    public String toString() {
+        return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
+                + " " + floatToString(mV3);
+    }
+
+    public static class Companion implements CompanionOperation {
+        public final int OP_CODE;
+
+        protected Companion(int code) {
+            OP_CODE = code;
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            float v1 = buffer.readFloat();
+            float v2 = buffer.readFloat();
+            float v3 = buffer.readFloat();
+
+            Operation op = construct(v1, v2, v3);
+            operations.add(op);
+        }
+
+        /**
+         * Construct and Operation from the 3 variables.
+         * This must be overridden by subclasses
+         * @param x1
+         * @param y1
+         * @param x2
+         * @return
+         */
+        public Operation construct(float x1,
+                                   float y1,
+                                   float x2) {
+            return null;
+        }
+
+        @Override
+        public String name() {
+            return "DrawRect";
+        }
+
+        @Override
+        public int id() {
+            return OP_CODE;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param x1
+         * @param y1
+         * @param x2
+         */
+        public void apply(WireBuffer buffer,
+                          float x1,
+                          float y1,
+                          float x2) {
+            buffer.start(OP_CODE);
+            buffer.writeFloat(x1);
+            buffer.writeFloat(y1);
+            buffer.writeFloat(x2);
+
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
new file mode 100644
index 0000000..ec35a16
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for draw commands that take 4 floats
+ */
+public abstract class DrawBase4 extends PaintOperation
+        implements VariableSupport {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_RECT) {
+                @Override
+                public Operation construct(float x1, float y1, float x2, float y2) {
+                    //   return new DrawRectBase(x1, y1, x2, y2);
+                    return null;
+                }
+            };
+    protected String mName = "DrawRectBase";
+    float mX1;
+    float mY1;
+    float mX2;
+    float mY2;
+    float mX1Value;
+    float mY1Value;
+    float mX2Value;
+    float mY2Value;
+
+    public DrawBase4(
+            float x1,
+            float y1,
+            float x2,
+            float y2) {
+        mX1Value = x1;
+        mY1Value = y1;
+        mX2Value = x2;
+        mY2Value = y2;
+
+        mX1 = x1;
+        mY1 = y1;
+        mX2 = x2;
+        mY2 = y2;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        mX1 = (Float.isNaN(mX1Value))
+                ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
+        mY1 = (Float.isNaN(mY1Value))
+                ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
+        mX2 = (Float.isNaN(mX2Value))
+                ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
+        mY2 = (Float.isNaN(mY2Value))
+                ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mX1Value)) {
+            context.listensTo(Utils.idFromNan(mX1Value), this);
+        }
+        if (Float.isNaN(mY1Value)) {
+            context.listensTo(Utils.idFromNan(mY1Value), this);
+        }
+        if (Float.isNaN(mX2Value)) {
+            context.listensTo(Utils.idFromNan(mX2Value), this);
+        }
+        if (Float.isNaN(mY2Value)) {
+            context.listensTo(Utils.idFromNan(mY2Value), this);
+        }
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mX1, mY1, mX2, mY2);
+    }
+
+    @Override
+    public String toString() {
+        return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1)
+                + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2);
+    }
+
+    public static class Companion implements CompanionOperation {
+        public final int OP_CODE;
+
+        protected Companion(int code) {
+            OP_CODE = code;
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            float sLeft = buffer.readFloat();
+            float srcTop = buffer.readFloat();
+            float srcRight = buffer.readFloat();
+            float srcBottom = buffer.readFloat();
+
+            Operation op = construct(sLeft, srcTop, srcRight, srcBottom);
+            operations.add(op);
+        }
+
+        /**
+         * Construct and Operation from the 3 variables.
+         * @param x1
+         * @param y1
+         * @param x2
+         * @param y2
+         * @return
+         */
+        public Operation construct(float x1,
+                                   float y1,
+                                   float x2,
+                                   float y2) {
+            return null;
+        }
+
+        @Override
+        public String name() {
+            return "DrawRect";
+        }
+
+        @Override
+        public int id() {
+            return OP_CODE;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param x1
+         * @param y1
+         * @param x2
+         * @param y2
+         */
+        public void apply(WireBuffer buffer,
+                          float x1,
+                          float y1,
+                          float x2,
+                          float y2) {
+            buffer.start(OP_CODE);
+            buffer.writeFloat(x1);
+            buffer.writeFloat(y1);
+            buffer.writeFloat(x2);
+            buffer.writeFloat(y2);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
new file mode 100644
index 0000000..2f4335e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Base class for draw commands the take 6 floats
+ */
+public abstract class DrawBase6 extends PaintOperation
+        implements VariableSupport {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_RECT) {
+                public Operation construct(float x1, float y1, float x2, float y2) {
+                    //   return new DrawRectBase(x1, y1, x2, y2);
+                    return null;
+                }
+            };
+    protected String mName = "DrawRectBase";
+    float mV1;
+    float mV2;
+    float mV3;
+    float mV4;
+    float mV5;
+    float mV6;
+    float mValue1;
+    float mValue2;
+    float mValue3;
+    float mValue4;
+    float mValue5;
+    float mValue6;
+
+    public DrawBase6(
+            float v1,
+            float v2,
+            float v3,
+            float v4,
+            float v5,
+            float v6) {
+        mValue1 = v1;
+        mValue2 = v2;
+        mValue3 = v3;
+        mValue4 = v4;
+        mValue5 = v5;
+        mValue6 = v6;
+
+        mV1 = v1;
+        mV2 = v2;
+        mV3 = v3;
+        mV4 = v4;
+        mV5 = v5;
+        mV6 = v6;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        mV1 = (Float.isNaN(mValue1))
+                ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+        mV2 = (Float.isNaN(mValue2))
+                ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+        mV3 = (Float.isNaN(mValue3))
+                ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+        mV4 = (Float.isNaN(mValue4))
+                ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
+        mV5 = (Float.isNaN(mValue5))
+                ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
+        mV6 = (Float.isNaN(mValue6))
+                ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mValue1)) {
+            context.listensTo(Utils.idFromNan(mValue1), this);
+        }
+        if (Float.isNaN(mValue2)) {
+            context.listensTo(Utils.idFromNan(mValue2), this);
+        }
+        if (Float.isNaN(mValue3)) {
+            context.listensTo(Utils.idFromNan(mValue3), this);
+        }
+        if (Float.isNaN(mValue4)) {
+            context.listensTo(Utils.idFromNan(mValue4), this);
+        }
+        if (Float.isNaN(mValue5)) {
+            context.listensTo(Utils.idFromNan(mValue5), this);
+        }
+        if (Float.isNaN(mValue6)) {
+            context.listensTo(Utils.idFromNan(mValue6), this);
+        }
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mV1, mV2, mV3, mV4, mV5, mV6);
+    }
+
+    @Override
+    public String toString() {
+        return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
+                + " " + floatToString(mV3) + " " + floatToString(mV4);
+    }
+
+    public static class Companion implements CompanionOperation {
+        public final int OP_CODE;
+
+        protected Companion(int code) {
+            OP_CODE = code;
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            float sv1 = buffer.readFloat();
+            float sv2 = buffer.readFloat();
+            float sv3 = buffer.readFloat();
+            float sv4 = buffer.readFloat();
+            float sv5 = buffer.readFloat();
+            float sv6 = buffer.readFloat();
+
+            Operation op = construct(sv1, sv2, sv3, sv4, sv5, sv6);
+            operations.add(op);
+        }
+
+        /**
+         * writes out a the operation to the buffer.
+         * @param v1
+         * @param v2
+         * @param v3
+         * @param v4
+         * @param v5
+         * @param v6
+         * @return
+         */
+        public Operation construct(float v1,
+                                   float v2,
+                                   float v3,
+                                   float v4,
+                                   float v5,
+                                   float v6) {
+            return null;
+        }
+
+        @Override
+        public String name() {
+            return "DrawRect";
+        }
+
+        @Override
+        public int id() {
+            return OP_CODE;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param v1
+         * @param v2
+         * @param v3
+         * @param v4
+         * @param v5
+         * @param v6
+         */
+        public void apply(WireBuffer buffer,
+                          float v1,
+                          float v2,
+                          float v3,
+                          float v4,
+                          float v5,
+                          float v6) {
+            buffer.start(OP_CODE);
+            buffer.writeFloat(v1);
+            buffer.writeFloat(v2);
+            buffer.writeFloat(v3);
+            buffer.writeFloat(v4);
+            buffer.writeFloat(v5);
+            buffer.writeFloat(v6);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index 2e971f5..ca40d12 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -20,16 +20,22 @@
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 
 import java.util.List;
 
-public class DrawBitmap extends PaintOperation {
+public class DrawBitmap extends PaintOperation implements VariableSupport {
     public static final Companion COMPANION = new Companion();
     float mLeft;
     float mTop;
     float mRight;
     float mBottom;
+    float mOutputLeft;
+    float mOutputTop;
+    float mOutputRight;
+    float mOutputBottom;
     int mId;
     int mDescriptionId = 0;
 
@@ -49,6 +55,34 @@
     }
 
     @Override
+    public void updateVariables(RemoteContext context) {
+        mOutputLeft = (Float.isNaN(mLeft))
+                ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
+        mOutputTop = (Float.isNaN(mTop))
+                ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
+        mOutputRight = (Float.isNaN(mRight))
+                ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
+        mOutputBottom = (Float.isNaN(mBottom))
+                ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mLeft)) {
+            context.listensTo(Utils.idFromNan(mLeft), this);
+        }
+        if (Float.isNaN(mTop)) {
+            context.listensTo(Utils.idFromNan(mTop), this);
+        }
+        if (Float.isNaN(mRight)) {
+            context.listensTo(Utils.idFromNan(mRight), this);
+        }
+        if (Float.isNaN(mBottom)) {
+            context.listensTo(Utils.idFromNan(mBottom), this);
+        }
+    }
+
+    @Override
     public void write(WireBuffer buffer) {
         COMPANION.apply(buffer, mId, mLeft, mTop, mRight, mBottom, mDescriptionId);
     }
@@ -105,9 +139,9 @@
 
     @Override
     public void paint(PaintContext context) {
-        context.drawBitmap(mId, mLeft,
-                mTop,
-                mRight,
-                mBottom);
+        context.drawBitmap(mId, mOutputLeft,
+                mOutputTop,
+                mOutputRight,
+                mOutputBottom);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
index 9ce754d..3a22e4f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
@@ -1,89 +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.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
+public class DrawCircle extends DrawBase3 {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_CIRCLE) {
+                @Override
+                public Operation construct(float x1,
+                                           float y1,
+                                           float x2
+                ) {
+                    return new DrawCircle(x1, y1, x2);
+                }
+            };
 
-public class DrawCircle extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mCenterX;
-    float mCenterY;
-    float mRadius;
-
-    public DrawCircle(float centerX, float centerY, float radius) {
-        mCenterX = centerX;
-        mCenterY = centerY;
-        mRadius = radius;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mCenterX,
-                mCenterY,
-                mRadius);
-    }
-
-    @Override
-    public String toString() {
-        return "";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float centerX = buffer.readFloat();
-            float centerY = buffer.readFloat();
-            float radius = buffer.readFloat();
-
-            DrawCircle op = new DrawCircle(centerX, centerY, radius);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "";
-        }
-
-        @Override
-        public int id() {
-            return 0;
-        }
-
-        public void apply(WireBuffer buffer, float centerX, float centerY, float radius) {
-            buffer.start(Operations.DRAW_CIRCLE);
-            buffer.writeFloat(centerX);
-            buffer.writeFloat(centerY);
-            buffer.writeFloat(radius);
-        }
+    public DrawCircle(
+            float left,
+            float top,
+            float right) {
+        super(left, top, right);
+        mName = "DrawCircle";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.drawCircle(mCenterX,
-                mCenterY,
-                mRadius);
+        context.drawCircle(mV1, mV2, mV3);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
index c7a8315..c70c6ea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
@@ -15,83 +15,28 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
-
-public class DrawLine extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mX1;
-    float mY1;
-    float mX2;
-    float mY2;
+public class DrawLine extends DrawBase4 {
+    public static final Companion COMPANION = new Companion(Operations.DRAW_LINE) {
+        @Override
+        public Operation construct(float x1,
+                                   float y1,
+                                   float x2,
+                                   float y2) {
+            return new DrawLine(x1, y1, x2, y2);
+        }
+    };
 
     public DrawLine(
-            float x1,
-            float y1,
-            float x2,
-            float y2) {
-        mX1 = x1;
-        mY1 = y1;
-        mX2 = x2;
-        mY2 = y2;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mX1,
-                mY1,
-                mX2,
-                mY2);
-    }
-
-    @Override
-    public String toString() {
-        return "DrawArc " + mX1 + " " + mY1
-                + " " + mX2 + " " + mY2 + ";";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float x1 = buffer.readFloat();
-            float y1 = buffer.readFloat();
-            float x2 = buffer.readFloat();
-            float y2 = buffer.readFloat();
-
-            DrawLine op = new DrawLine(x1, y1, x2, y2);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "DrawLine";
-        }
-
-        @Override
-        public int id() {
-            return Operations.DRAW_LINE;
-        }
-
-        public void apply(WireBuffer buffer,
-                          float x1,
-                          float y1,
-                          float x2,
-                          float y2) {
-            buffer.start(Operations.DRAW_LINE);
-            buffer.writeFloat(x1);
-            buffer.writeFloat(y1);
-            buffer.writeFloat(x2);
-            buffer.writeFloat(y2);
-        }
+            float left,
+            float top,
+            float right,
+            float bottom) {
+        super(left, top, right, bottom);
+        mName = "DrawLine";
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
index 7143753..ba17994 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
@@ -15,88 +15,33 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
-
-public class DrawOval extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mLeft;
-    float mTop;
-    float mRight;
-    float mBottom;
-
+public class DrawOval extends DrawBase4 {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_OVAL) {
+                @Override
+                public Operation construct(float x1,
+                                           float y1,
+                                           float x2,
+                                           float y2) {
+                    return new DrawOval(x1, y1, x2, y2);
+                }
+            };
 
     public DrawOval(
             float left,
             float top,
             float right,
             float bottom) {
-        mLeft = left;
-        mTop = top;
-        mRight = right;
-        mBottom = bottom;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
-    }
-
-    @Override
-    public String toString() {
-        return "DrawOval " + mLeft + " " + mTop
-                + " " + mRight + " " + mBottom + ";";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float sLeft = buffer.readFloat();
-            float srcTop = buffer.readFloat();
-            float srcRight = buffer.readFloat();
-            float srcBottom = buffer.readFloat();
-
-            DrawOval op = new DrawOval(sLeft, srcTop, srcRight, srcBottom);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "DrawOval";
-        }
-
-        @Override
-        public int id() {
-            return Operations.DRAW_OVAL;
-        }
-
-        public void apply(WireBuffer buffer,
-                          float left,
-                          float top,
-                          float right,
-                          float bottom) {
-            buffer.start(Operations.DRAW_OVAL);
-            buffer.writeFloat(left);
-            buffer.writeFloat(top);
-            buffer.writeFloat(right);
-            buffer.writeFloat(bottom);
-        }
+        super(left, top, right, bottom);
+        mName = "DrawOval";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.drawOval(mLeft,
-                mTop,
-                mRight,
-                mBottom);
+        context.drawOval(mX1, mY1, mX2, mY2);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index 7b8a9e9..6dbc5a6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -41,7 +41,7 @@
 
     @Override
     public String toString() {
-        return "DrawPath " + ";";
+        return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd;
     }
 
     public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
index 4775241..633aed4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
@@ -15,88 +15,37 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
-
-public class DrawRect extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mLeft;
-    float mTop;
-    float mRight;
-    float mBottom;
+/**
+ * Draw a Rectangle
+ */
+public class DrawRect extends DrawBase4 {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_RECT) {
+                @Override
+                public Operation construct(float x1,
+                                           float y1,
+                                           float x2,
+                                           float y2) {
+                    return new DrawRect(x1, y1, x2, y2);
+                }
+            };
 
     public DrawRect(
             float left,
             float top,
             float right,
             float bottom) {
-        mLeft = left;
-        mTop = top;
-        mRight = right;
-        mBottom = bottom;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom);
-    }
-
-    @Override
-    public String toString() {
-        return "DrawRect " + mLeft + " " + mTop
-                + " " + mRight + " " + mBottom + ";";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float sLeft = buffer.readFloat();
-            float srcTop = buffer.readFloat();
-            float srcRight = buffer.readFloat();
-            float srcBottom = buffer.readFloat();
-
-            DrawRect op = new DrawRect(sLeft, srcTop, srcRight, srcBottom);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "DrawRect";
-        }
-
-        @Override
-        public int id() {
-            return Operations.DRAW_RECT;
-        }
-
-        public void apply(WireBuffer buffer,
-                          float left,
-                          float top,
-                          float right,
-                          float bottom) {
-            buffer.start(Operations.DRAW_RECT);
-            buffer.writeFloat(left);
-            buffer.writeFloat(top);
-            buffer.writeFloat(right);
-            buffer.writeFloat(bottom);
-        }
+        super(left, top, right, bottom);
+        mName = "DrawRect";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.drawRect(mLeft,
-                mTop,
-                mRight,
-                mBottom);
+        context.drawRect(mX1, mY1, mX2, mY2);
     }
 
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index 8da16e7..b9d0a67 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -15,104 +15,40 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
+/**
+ * Draw a rounded rectangle
+ */
+public class DrawRoundRect extends DrawBase6 {
+    public static final Companion COMPANION =
+            new Companion(Operations.DRAW_ROUND_RECT) {
+                @Override
+                public Operation construct(float v1,
+                                           float v2,
+                                           float v3,
+                                           float v4,
+                                           float v5,
+                                           float v6) {
+                    return new DrawRoundRect(v1, v2, v3, v4, v5, v6);
+                }
+            };
 
-public class DrawRoundRect extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mLeft;
-    float mTop;
-    float mRight;
-    float mBottom;
-    float mRadiusX;
-    float mRadiusY;
-
-    public DrawRoundRect(
-            float left,
-            float top,
-            float right,
-            float bottom,
-            float radiusX,
-            float radiusY) {
-        mLeft = left;
-        mTop = top;
-        mRight = right;
-        mBottom = bottom;
-        mRadiusX = radiusX;
-        mRadiusY = radiusY;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom, mRadiusX, mRadiusY);
-    }
-
-    @Override
-    public String toString() {
-        return "DrawRoundRect " + mLeft + " " + mTop
-                + " " + mRight + " " + mBottom
-                + " (" + mRadiusX + " " + mRadiusY + ");";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float sLeft = buffer.readFloat();
-            float srcTop = buffer.readFloat();
-            float srcRight = buffer.readFloat();
-            float srcBottom = buffer.readFloat();
-            float srcRadiusX = buffer.readFloat();
-            float srcRadiusY = buffer.readFloat();
-
-            DrawRoundRect op = new DrawRoundRect(sLeft, srcTop, srcRight,
-                    srcBottom, srcRadiusX, srcRadiusY);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "DrawOval";
-        }
-
-        @Override
-        public int id() {
-            return Operations.DRAW_ROUND_RECT;
-        }
-
-        public void apply(WireBuffer buffer,
-                          float left,
-                          float top,
-                          float right,
-                          float bottom,
-                          float radiusX,
-                          float radiusY) {
-            buffer.start(Operations.DRAW_ROUND_RECT);
-            buffer.writeFloat(left);
-            buffer.writeFloat(top);
-            buffer.writeFloat(right);
-            buffer.writeFloat(bottom);
-            buffer.writeFloat(radiusX);
-            buffer.writeFloat(radiusY);
-        }
+    public DrawRoundRect(float v1,
+                         float v2,
+                         float v3,
+                         float v4,
+                         float v5,
+                         float v6) {
+        super(v1, v2, v3, v4, v5, v6);
+        mName = "ClipRect";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.drawRoundRect(mLeft,
-                mTop,
-                mRight,
-                mBottom,
-                mRadiusX,
-                mRadiusY
+        context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6
         );
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
new file mode 100644
index 0000000..f8f8afd
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Draw Text
+ */
+public class DrawText extends PaintOperation {
+    public static final Companion COMPANION = new Companion();
+    int mTextID;
+    int mStart = 0;
+    int mEnd = 0;
+    int mContextStart = 0;
+    int mContextEnd = 0;
+    float mX = 0f;
+    float mY = 0f;
+    boolean mRtl = false;
+
+    public DrawText(int textID,
+                    int start,
+                    int end,
+                    int contextStart,
+                    int contextEnd,
+                    float x,
+                    float y,
+                    boolean rtl) {
+        mTextID = textID;
+        mStart = start;
+        mEnd = end;
+        mContextStart = contextStart;
+        mContextEnd = contextEnd;
+        mX = x;
+        mY = y;
+        mRtl = rtl;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
+
+    }
+
+    @Override
+    public String toString() {
+        return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", " + mX + ", " + mY;
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int text = buffer.readInt();
+            int start = buffer.readInt();
+            int end = buffer.readInt();
+            int contextStart = buffer.readInt();
+            int contextEnd = buffer.readInt();
+            float x = buffer.readFloat();
+            float y = buffer.readFloat();
+            boolean rtl = buffer.readBoolean();
+            DrawText op = new DrawText(text, start, end, contextStart, contextEnd, x, y, rtl);
+
+            operations.add(op);
+        }
+
+        @Override
+        public String name() {
+            return "";
+        }
+
+        @Override
+        public int id() {
+            return 0;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param textID
+         * @param start
+         * @param end
+         * @param contextStart
+         * @param contextEnd
+         * @param x
+         * @param y
+         * @param rtl
+         */
+        public void apply(WireBuffer buffer,
+                          int textID,
+                          int start,
+                          int end,
+                          int contextStart,
+                          int contextEnd,
+                          float x,
+                          float y,
+                          boolean rtl) {
+            buffer.start(Operations.DRAW_TEXT_RUN);
+            buffer.writeInt(textID);
+            buffer.writeInt(start);
+            buffer.writeInt(end);
+            buffer.writeInt(contextStart);
+            buffer.writeInt(contextEnd);
+            buffer.writeFloat(x);
+            buffer.writeFloat(y);
+            buffer.writeBoolean(rtl);
+        }
+    }
+
+    @Override
+    public void paint(PaintContext context) {
+        context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
new file mode 100644
index 0000000..4f0641f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Draw Text in Anchored to a point
+ */
+public class DrawTextAnchored extends PaintOperation implements VariableSupport {
+    public static final Companion COMPANION = new Companion();
+    int mTextID;
+    float mX;
+    float mY;
+    float mPanX;
+    float mPanY;
+    int mFlags;
+    float mOutX;
+    float mOutY;
+    float mOutPanX;
+    float mOutPanY;
+
+    public static final int ANCHOR_TEXT_RTL = 1;
+    public static final int ANCHOR_MONOSPACE_MEASURE = 2;
+
+    public DrawTextAnchored(int textID,
+                            float x,
+                            float y,
+                            float panX,
+                            float panY,
+                            int flags) {
+        mTextID = textID;
+        mX = x;
+        mY = y;
+        mOutX = mX;
+        mOutY = mY;
+        mFlags = flags;
+        mOutPanX = mPanX = panX;
+        mOutPanY = mPanY = panY;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        mOutX = (Float.isNaN(mX))
+                ? context.getFloat(Utils.idFromNan(mX)) : mX;
+        mOutY = (Float.isNaN(mY))
+                ? context.getFloat(Utils.idFromNan(mY)) : mY;
+        mOutPanX = (Float.isNaN(mPanX))
+                ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
+        mOutPanY = (Float.isNaN(mPanY))
+                ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mX)) {
+            context.listensTo(Utils.idFromNan(mX), this);
+        }
+        if (Float.isNaN(mY)) {
+            context.listensTo(Utils.idFromNan(mY), this);
+        }
+        if (Float.isNaN(mPanX)) {
+            context.listensTo(Utils.idFromNan(mPanX), this);
+        }
+        if (Float.isNaN(mPanY) && Utils.idFromNan(mPanY) > 0) {
+            context.listensTo(Utils.idFromNan(mPanY), this);
+        }
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mTextID, mX,
+                mY,
+                mPanX,
+                mPanY,
+                mFlags);
+    }
+
+    @Override
+    public String toString() {
+        return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", "
+                + floatToStr(mY) + ", "
+                + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", "
+                + Integer.toBinaryString(mFlags);
+    }
+
+    private static String floatToStr(float v) {
+        if (Float.isNaN(v)) {
+            return "[" + Utils.idFromNan(v) + "]";
+        }
+        return Float.toString(v);
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int textID = buffer.readInt();
+            float x = buffer.readFloat();
+            float y = buffer.readFloat();
+            float panX = buffer.readFloat();
+            float panY = buffer.readFloat();
+            int flags = buffer.readInt();
+
+            DrawTextAnchored op = new DrawTextAnchored(textID,
+                    x, y,
+                    panX, panY,
+                    flags);
+
+            operations.add(op);
+        }
+
+        @Override
+        public String name() {
+            return "";
+        }
+
+        @Override
+        public int id() {
+            return 0;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param textID
+         * @param x
+         * @param y
+         * @param panX
+         * @param panY
+         * @param flags
+         */
+        public void apply(WireBuffer buffer,
+                          int textID,
+                          float x,
+                          float y,
+                          float panX,
+                          float panY,
+                          int flags) {
+            buffer.start(Operations.DRAW_TEXT_ANCHOR);
+            buffer.writeInt(textID);
+            buffer.writeFloat(x);
+            buffer.writeFloat(y);
+            buffer.writeFloat(panX);
+            buffer.writeFloat(panY);
+            buffer.writeInt(flags);
+        }
+    }
+
+    float[] mBounds = new float[4];
+
+    private float getHorizontalOffset() {
+        // TODO scale  TextSize / BaseTextSize;
+        float scale = 1.0f;
+
+        float textWidth = scale * (mBounds[2] - mBounds[0]);
+        float boxWidth = 0;
+        return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f
+                - (scale * mBounds[0]);
+    }
+
+    private float getVerticalOffset() {
+        // TODO scale TextSize / BaseTextSize;
+        float scale = 1.0f;
+        float boxHeight = 0;
+        float textHeight = scale * (mBounds[3] - mBounds[1]);
+        return (boxHeight - textHeight) * (1 - mOutPanY) / 2
+                - (scale * mBounds[1]);
+    }
+
+    @Override
+    public void paint(PaintContext context) {
+        context.getTextBounds(mTextID, 0, -1,
+                (mFlags & ANCHOR_MONOSPACE_MEASURE) != 0, mBounds);
+        float x = mOutX + getHorizontalOffset();
+        float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset();
+        context.drawTextRun(mTextID, 0, -1, 0, 1, x, y,
+                (mFlags & ANCHOR_TEXT_RTL) == 1);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index 1856e30..b1a0172 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -24,6 +24,9 @@
 
 import java.util.List;
 
+/**
+ * Draw text along a path.
+ */
 public class DrawTextOnPath extends PaintOperation {
     public static final Companion COMPANION = new Companion();
     int mPathId;
@@ -45,7 +48,8 @@
 
     @Override
     public String toString() {
-        return "DrawTextOnPath " + " " + mPathId + ";";
+        return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] "
+                + mHOffset + ", " + mVOffset;
     }
 
     public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index ef0a4ad..48fc94e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -58,7 +58,7 @@
     public String toString() {
         return "DrawTweenPath " + mPath1Id + " " + mPath2Id
                 + " " + mTween + " " + mStart + " "
-                + "- " + mStop + ";";
+                + "- " + mStop;
     }
 
     public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
new file mode 100644
index 0000000..576b53f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -0,0 +1,93 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class FloatConstant implements Operation {
+    public int mTextId;
+    public float mValue;
+    public static final Companion COMPANION = new Companion();
+    public static final int MAX_STRING_SIZE = 4000;
+
+    public FloatConstant(int textId, float value) {
+        this.mTextId = textId;
+        this.mValue = value;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mTextId, mValue);
+    }
+
+    @Override
+    public String toString() {
+        return "FloatConstant[" + mTextId + "] = " + mValue + "";
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {}
+
+        @Override
+        public String name() {
+            return "FloatExpression";
+        }
+
+        @Override
+        public int id() {
+            return Operations.DATA_FLOAT;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param textId
+         * @param value
+         */
+        public void apply(WireBuffer buffer, int textId, float value) {
+            buffer.start(Operations.DATA_FLOAT);
+            buffer.writeInt(textId);
+            buffer.writeFloat(value);
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int textId = buffer.readInt();
+
+            float value = buffer.readFloat();
+            operations.add(new FloatConstant(textId, value));
+        }
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        context.loadFloat(mTextId, mValue);
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
new file mode 100644
index 0000000..354f41b
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -0,0 +1,206 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Operation to deal with AnimatedFloats
+ * This is designed to be an optimized calculation for things like
+ * injecting the width of the component int draw rect
+ * As well as supporting generalized animation floats.
+ * The floats represent a RPN style calculator
+ */
+public class FloatExpression implements Operation, VariableSupport {
+    public int mId;
+    public float[] mSrcValue;
+    public float[] mSrcAnimation;
+    public FloatAnimation mFloatAnimation;
+    public float[] mPreCalcValue;
+    private float mLastChange = Float.NaN;
+    AnimatedFloatExpression mExp = new AnimatedFloatExpression();
+    public static final Companion COMPANION = new Companion();
+    public static final int MAX_STRING_SIZE = 4000;
+
+    public FloatExpression(int id, float[] value, float[] animation) {
+        this.mId = id;
+        this.mSrcValue = value;
+        this.mSrcAnimation = animation;
+        if (mSrcAnimation != null) {
+            mFloatAnimation = new FloatAnimation(mSrcAnimation);
+        }
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) {
+            mPreCalcValue = new float[mSrcValue.length];
+        }
+        //Utils.log("updateVariables ");
+        boolean value_changed = false;
+        for (int i = 0; i < mSrcValue.length; i++) {
+            float v = mSrcValue[i];
+            if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) {
+                float newValue = context.getFloat(Utils.idFromNan(v));
+                if (mFloatAnimation != null) {
+                    if (mPreCalcValue[i] != newValue) {
+                        mLastChange = context.getAnimationTime();
+                        value_changed = true;
+                        mPreCalcValue[i] = newValue;
+                    }
+                } else {
+                    mPreCalcValue[i] = newValue;
+                }
+            } else {
+                mPreCalcValue[i] = mSrcValue[i];
+            }
+        }
+        if (value_changed && mFloatAnimation != null) {
+            float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
+            if (Float.isNaN(mFloatAnimation.getTargetValue())) {
+                mFloatAnimation.setInitialValue(v);
+            } else {
+                mFloatAnimation.setInitialValue(mFloatAnimation.getTargetValue());
+            }
+            mFloatAnimation.setTargetValue(v);
+        }
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        for (int i = 0; i < mSrcValue.length; i++) {
+            float v = mSrcValue[i];
+            if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) {
+                context.listensTo(Utils.idFromNan(v), this);
+            }
+        }
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        updateVariables(context);
+        float t = context.getAnimationTime();
+        if (Float.isNaN(mLastChange)) {
+            mLastChange = t;
+        }
+        if (mFloatAnimation != null) {
+            float f = mFloatAnimation.get(t - mLastChange);
+            context.loadFloat(mId, f);
+        } else {
+            context.loadFloat(mId, mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)));
+        }
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mId, mSrcValue, mSrcAnimation);
+    }
+
+    @Override
+    public String toString() {
+        String[] labels = new String[mSrcValue.length];
+        for (int i = 0; i < mSrcValue.length; i++) {
+            if (Float.isNaN(mSrcValue[i])) {
+                labels[i] = "[" + Utils.idFromNan(mSrcValue[i]) + "]";
+            }
+
+        }
+        return "FloatExpression[" + mId + "] = ("
+                + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")";
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "FloatExpression";
+        }
+
+        @Override
+        public int id() {
+            return Operations.ANIMATED_FLOAT;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param id
+         * @param value
+         * @param animation
+         */
+        public void apply(WireBuffer buffer, int id, float[] value, float[] animation) {
+            buffer.start(Operations.ANIMATED_FLOAT);
+            buffer.writeInt(id);
+
+            int len = value.length;
+            if (animation != null) {
+                len |= (animation.length << 16);
+            }
+            buffer.writeInt(len);
+
+            for (int i = 0; i < value.length; i++) {
+                buffer.writeFloat(value[i]);
+            }
+            if (animation != null) {
+                for (int i = 0; i < animation.length; i++) {
+                    buffer.writeFloat(animation[i]);
+                }
+            }
+
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int id = buffer.readInt();
+            int len = buffer.readInt();
+            int valueLen = len & 0xFFFF;
+            int animLen = (len >> 16) & 0xFFFF;
+            float[] values = new float[valueLen];
+            for (int i = 0; i < values.length; i++) {
+                values[i] = buffer.readFloat();
+            }
+
+            float[] animation;
+            if (animLen != 0) {
+                animation = new float[animLen];
+                for (int i = 0; i < animation.length; i++) {
+                    animation[i] = buffer.readFloat();
+                }
+            } else {
+                animation = null;
+            }
+            operations.add(new FloatExpression(id, values, animation));
+        }
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index 482e0e2..0dad45c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -37,7 +37,7 @@
 
     @Override
     public String toString() {
-        return "MatrixRestore;";
+        return "MatrixRestore";
     }
 
     public static class Companion implements CompanionOperation {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
index d6c89e0..bbf4135 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
@@ -15,68 +15,29 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
-
-public class MatrixRotate extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mRotate, mPivotX, mPivotY;
+public class MatrixRotate extends DrawBase3 {
+    public static final Companion COMPANION =
+            new Companion(Operations.MATRIX_ROTATE) {
+                @Override
+                public Operation construct(float rotate,
+                                           float pivotX,
+                                           float pivotY
+                ) {
+                    return new MatrixRotate(rotate, pivotX, pivotY);
+                }
+            };
 
     public MatrixRotate(float rotate, float pivotX, float pivotY) {
-        mRotate = rotate;
-        mPivotX = pivotX;
-        mPivotY = pivotY;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mRotate, mPivotX, mPivotY);
-    }
-
-    @Override
-    public String toString() {
-        return "DrawArc " + mRotate + ", " + mPivotX + ", " + mPivotY + ";";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float rotate = buffer.readFloat();
-            float pivotX = buffer.readFloat();
-            float pivotY = buffer.readFloat();
-            MatrixRotate op = new MatrixRotate(rotate, pivotX, pivotY);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "Matrix";
-        }
-
-        @Override
-        public int id() {
-            return Operations.MATRIX_ROTATE;
-        }
-
-        public void apply(WireBuffer buffer, float rotate, float pivotX, float pivotY) {
-            buffer.start(Operations.MATRIX_ROTATE);
-            buffer.writeFloat(rotate);
-            buffer.writeFloat(pivotX);
-            buffer.writeFloat(pivotY);
-        }
+        super(rotate, pivotX, pivotY);
+        mName = "MatrixRotate";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.matrixRotate(mRotate, mPivotX, mPivotY);
+        context.matrixRotate(mV1, mV2, mV3);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
index 28aa68dd..04b940b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
@@ -15,74 +15,30 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
-
-public class MatrixScale extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mScaleX, mScaleY;
-    float mCenterX, mCenterY;
+public class MatrixScale extends DrawBase4 {
+    public static final Companion COMPANION =
+            new Companion(Operations.MATRIX_SCALE) {
+                @Override
+                public Operation construct(float scaleX,
+                                           float scaleY,
+                                           float centerX,
+                                           float centerY
+                ) {
+                    return new MatrixScale(scaleX, scaleY, centerX, centerY);
+                }
+            };
 
     public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) {
-        mScaleX = scaleX;
-        mScaleY = scaleY;
-        mCenterX = centerX;
-        mCenterY = centerY;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mScaleX, mScaleY, mCenterX, mCenterY);
-    }
-
-    @Override
-    public String toString() {
-        return "MatrixScale " + mScaleY + ", " + mScaleY + ";";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float scaleX = buffer.readFloat();
-            float scaleY = buffer.readFloat();
-            float centerX = buffer.readFloat();
-            float centerY = buffer.readFloat();
-            MatrixScale op = new MatrixScale(scaleX, scaleY, centerX, centerY);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "Matrix";
-        }
-
-        @Override
-        public int id() {
-            return Operations.MATRIX_SCALE;
-        }
-
-        public void apply(WireBuffer buffer, float scaleX, float scaleY,
-                float centerX, float centerY) {
-            buffer.start(Operations.MATRIX_SCALE);
-            buffer.writeFloat(scaleX);
-            buffer.writeFloat(scaleY);
-            buffer.writeFloat(centerX);
-            buffer.writeFloat(centerY);
-
-        }
+        super(scaleX, scaleY, centerX, centerY);
+        mName = "MatrixScale";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.mtrixScale(mScaleX, mScaleY, mCenterX, mCenterY);
+        context.matrixScale(mX1, mY1, mX2, mY2);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
index 3298752..4f34e98 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
@@ -15,65 +15,28 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
-import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
-import com.android.internal.widget.remotecompose.core.PaintOperation;
-import com.android.internal.widget.remotecompose.core.WireBuffer;
 
-import java.util.List;
-
-public class MatrixTranslate extends PaintOperation {
-    public static final Companion COMPANION = new Companion();
-    float mTranslateX, mTranslateY;
+public class MatrixTranslate extends DrawBase2 {
+    public static final Companion COMPANION =
+            new Companion(Operations.MATRIX_TRANSLATE) {
+                @Override
+                public Operation construct(float x1,
+                                           float y1
+                ) {
+                    return new MatrixTranslate(x1, y1);
+                }
+            };
 
     public MatrixTranslate(float translateX, float translateY) {
-        mTranslateX = translateX;
-        mTranslateY = translateY;
-    }
-
-    @Override
-    public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mTranslateX, mTranslateY);
-    }
-
-    @Override
-    public String toString() {
-        return "DrawArc " + mTranslateY + ", " + mTranslateY + ";";
-    }
-
-    public static class Companion implements CompanionOperation {
-        private Companion() {
-        }
-
-        @Override
-        public void read(WireBuffer buffer, List<Operation> operations) {
-            float translateX = buffer.readFloat();
-            float translateY = buffer.readFloat();
-            MatrixTranslate op = new MatrixTranslate(translateX, translateY);
-            operations.add(op);
-        }
-
-        @Override
-        public String name() {
-            return "Matrix";
-        }
-
-        @Override
-        public int id() {
-            return Operations.MATRIX_TRANSLATE;
-        }
-
-        public void apply(WireBuffer buffer, float translateX, float translateY) {
-            buffer.start(Operations.MATRIX_TRANSLATE);
-            buffer.writeFloat(translateX);
-            buffer.writeFloat(translateY);
-        }
+        super(translateX, translateY);
+        mName = "MatrixTranslate";
     }
 
     @Override
     public void paint(PaintContext context) {
-        context.matrixTranslate(mTranslateX, mTranslateY);
+        context.matrixTranslate(mV1, mV2);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
new file mode 100644
index 0000000..0c5b286
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -0,0 +1,99 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class NamedVariable implements Operation {
+    public int mVarId;
+    public String mVarName;
+    public int mVarType;
+    public static final Companion COMPANION = new Companion();
+    public static final int MAX_STRING_SIZE = 4000;
+
+    public NamedVariable(int varId, int varType, String name) {
+        this.mVarId = varId;
+        this.mVarType = varType;
+        this.mVarName = name;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mVarId, mVarType, mVarName);
+    }
+
+    @Override
+    public String toString() {
+        return "VariableName[" + mVarId + "] = \""
+                + Utils.trimString(mVarName, 10) + "\" type=" + mVarType;
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "TextData";
+        }
+
+        @Override
+        public int id() {
+            return Operations.DATA_TEXT;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param varId
+         * @param varType
+         * @param text
+         */
+        public void apply(WireBuffer buffer, int varId, int varType, String text) {
+            buffer.start(Operations.DATA_TEXT);
+            buffer.writeInt(varId);
+            buffer.writeInt(varType);
+            buffer.writeUTF8(text);
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int varId = buffer.readInt();
+            int varType = buffer.readInt();
+            String text = buffer.readUTF8(MAX_STRING_SIZE);
+            operations.add(new NamedVariable(varId, varType, text));
+        }
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        context.loadVariableName(mVarName, mVarId, mVarType);
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index e5683ec..0807bcd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -20,12 +20,14 @@
 import com.android.internal.widget.remotecompose.core.Operations;
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 
 import java.util.List;
 
-public class PaintData extends PaintOperation {
+public class PaintData extends PaintOperation implements VariableSupport {
     public PaintBundle mPaintData = new PaintBundle();
     public static final Companion COMPANION = new Companion();
     public static final int MAX_STRING_SIZE = 4000;
@@ -34,6 +36,16 @@
     }
 
     @Override
+    public void updateVariables(RemoteContext context) {
+        mPaintData.updateVariables(context);
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        mPaintData.registerVars(context, this);
+    }
+
+    @Override
     public void write(WireBuffer buffer) {
         COMPANION.apply(buffer, mPaintData);
     }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 2646b27..e467e7b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -18,27 +18,50 @@
 import com.android.internal.widget.remotecompose.core.CompanionOperation;
 import com.android.internal.widget.remotecompose.core.Operation;
 import com.android.internal.widget.remotecompose.core.Operations;
-import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
 
+import java.util.Arrays;
 import java.util.List;
 
-public class PathData implements Operation {
+public class PathData implements Operation, VariableSupport {
     public static final Companion COMPANION = new Companion();
     int mInstanceId;
-    float[] mRef;
     float[] mFloatPath;
-    float[] mRetFloats;
+    float[] mOutputPath;
 
     PathData(int instanceId, float[] floatPath) {
         mInstanceId = instanceId;
         mFloatPath = floatPath;
+        mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length);
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        for (int i = 0; i < mFloatPath.length; i++) {
+            float v = mFloatPath[i];
+            if (Utils.isVariable(v)) {
+                mOutputPath[i] = (Float.isNaN(v))
+                        ? context.getFloat(Utils.idFromNan(v)) : v;
+            } else {
+                mOutputPath[i] = v;
+            }
+        }
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        for (int i = 0; i < mFloatPath.length; i++) {
+            if (Float.isNaN(mFloatPath[i])) {
+                context.listensTo(Utils.idFromNan(mFloatPath[i]), this);
+            }
+        }
     }
 
     @Override
     public void write(WireBuffer buffer) {
-        COMPANION.apply(buffer, mInstanceId, mFloatPath);
+        COMPANION.apply(buffer, mInstanceId, mOutputPath);
     }
 
     @Override
@@ -46,29 +69,35 @@
         return pathString(mFloatPath);
     }
 
-    public float[] getFloatPath(PaintContext context) {
-        float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
-        if (ret == null) {
-            return mFloatPath; // Assume floatPath is declared elsewhere
-        }
-        float[] localRef = mRef; // Assume ref is of type Float[]
-        if (localRef == null) {
-            for (int i = 0; i < mFloatPath.length; i++) {
-                ret[i] = mFloatPath[i];
-            }
-        } else {
-            for (int i = 0; i < mFloatPath.length; i++) {
-                float lr = localRef[i];
-                if (Float.isNaN(lr)) {
-                    ret[i] = Utils.getActualValue(lr);
-                } else {
-                    ret[i] = mFloatPath[i];
-                }
-            }
-        }
-        return ret;
+    @Override
+    public String toString() {
+        return "PathData[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\"";
     }
 
+    /**
+     * public float[] getFloatPath(PaintContext context) {
+     * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
+     * if (ret == null) {
+     * return mFloatPath; // Assume floatPath is declared elsewhere
+     * }
+     * float[] localRef = mRef; // Assume ref is of type Float[]
+     * if (localRef == null) {
+     * for (int i = 0; i < mFloatPath.length; i++) {
+     * ret[i] = mFloatPath[i];
+     * }
+     * } else {
+     * for (int i = 0; i < mFloatPath.length; i++) {
+     * float lr = localRef[i];
+     * if (Float.isNaN(lr)) {
+     * ret[i] = Utils.getActualValue(lr);
+     * } else {
+     * ret[i] = mFloatPath[i];
+     * }
+     * }
+     * }
+     * return ret;
+     * }
+     */
     public static final int MOVE = 10;
     public static final int LINE = 11;
     public static final int QUADRATIC = 12;
@@ -155,7 +184,7 @@
                             str.append(".");
                             break;
                         default:
-                            str.append("X");
+                            str.append("[" + id + "]");
                             break;
                     }
                 } else {
@@ -170,7 +199,7 @@
 
     @Override
     public void apply(RemoteContext context) {
-        context.loadPathData(mInstanceId, mFloatPath);
+        context.loadPathData(mInstanceId, mOutputPath);
     }
 
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 6d924eb..997e8dc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -94,7 +94,6 @@
     public static final int SCALE_CROP = 5;
     public static final int SCALE_FILL_BOUNDS = 6;
 
-
     public static final Companion COMPANION = new Companion();
 
     /**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
index 64c7f3e..076b28e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
@@ -48,7 +48,7 @@
 
     @Override
     public String toString() {
-        return "ROOT_CONTENT_DESCRIPTION " + mContentDescription;
+        return "RootContentDescription " + mContentDescription;
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
new file mode 100644
index 0000000..8463ac5
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -0,0 +1,309 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Operation to deal with bitmap data
+ * On getting an Image during a draw call the bitmap is compressed and saved
+ * in playback the image is decompressed
+ */
+public class ShaderData implements Operation, VariableSupport {
+    int mShaderTextId; // the actual text of a shader
+    int mShaderID; // allows shaders to be referenced by number
+    HashMap<String, float[]> mUniformRawFloatMap = null;
+    HashMap<String, float[]> mUniformFloatMap = null;
+    HashMap<String, int[]> mUniformIntMap = null;
+    HashMap<String, Integer> mUniformBitmapMap = null;
+
+    public static final int MAX_IMAGE_DIMENSION = 8000;
+
+    public static final Companion COMPANION = new Companion();
+
+    public ShaderData(int shaderID,
+                      int shaderTextId,
+                      HashMap<String, float[]> floatMap,
+                      HashMap<String, int[]> intMap,
+                      HashMap<String, Integer> bitmapMap) {
+        mShaderID = shaderID;
+        mShaderTextId = shaderTextId;
+        if (floatMap != null) {
+            mUniformFloatMap = new HashMap<>();
+            mUniformRawFloatMap = new HashMap<>();
+
+            for (String name : floatMap.keySet()) {
+                mUniformRawFloatMap.put(name, floatMap.get(name));
+                mUniformFloatMap.put(name, floatMap.get(name));
+            }
+        }
+
+        if (intMap != null) {
+            mUniformIntMap = new HashMap<>();
+            for (String name : intMap.keySet()) {
+                mUniformIntMap.put(name, intMap.get(name));
+            }
+        }
+        if (bitmapMap != null) {
+            mUniformBitmapMap = new HashMap<>();
+            for (String name : bitmapMap.keySet()) {
+                mUniformBitmapMap.put(name, bitmapMap.get(name));
+            }
+        }
+
+    }
+
+    public int getShaderTextId() {
+        return mShaderTextId;
+    }
+
+    /**
+     * get names of all known floats
+     * @return
+     */
+    public String[] getUniformFloatNames() {
+        if (mUniformFloatMap == null) return new String[0];
+        return mUniformFloatMap.keySet().toArray(new String[0]);
+    }
+
+    /**
+     * Get float values associated with the name
+     * @param name
+     * @return
+     */
+    public float[] getUniformFloats(String name) {
+        return mUniformFloatMap.get(name);
+    }
+
+    /**
+     * get the name of all know uniform integers
+     * @return
+     */
+    public String[] getUniformIntegerNames() {
+        if (mUniformIntMap == null) return new String[0];
+        return mUniformIntMap.keySet().toArray(new String[0]);
+    }
+
+    /**
+     * Get Int value associated with the name
+     * @param name
+     * @return
+     */
+    public int[] getUniformInts(String name) {
+        return mUniformIntMap.get(name);
+    }
+
+    /**
+     * get list of uniform Bitmaps
+     * @return
+     */
+    public String[] getUniformBitmapNames() {
+        if (mUniformBitmapMap == null) return new String[0];
+        return mUniformBitmapMap.keySet().toArray(new String[0]);
+    }
+
+    /**
+     * Get a bitmap stored under that name
+     * @param name
+     * @return
+     */
+    public int getUniformBitmapId(String name) {
+        return mUniformBitmapMap.get(name);
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mShaderID, mShaderTextId,
+                mUniformFloatMap, mUniformIntMap, mUniformBitmapMap);
+    }
+
+    @Override
+    public String toString() {
+        return "SHADER DATA " + mShaderID;
+    }
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        for (String name : mUniformRawFloatMap.keySet()) {
+            float[] value = mUniformRawFloatMap.get(name);
+            float[] out = null;
+            for (int i = 0; i < value.length; i++) {
+                if (Float.isNaN(value[i])) {
+                    if (out == null) { // need to copy
+                        out = Arrays.copyOf(value, value.length);
+                    }
+                    out[i] = context.getFloat(Utils.idFromNan(value[i]));
+                }
+            }
+            mUniformFloatMap.put(name, out == null ? value : out);
+        }
+    }
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        for (String name : mUniformRawFloatMap.keySet()) {
+            float[] value = mUniformRawFloatMap.get(name);
+            for (int i = 0; i < value.length; i++) {
+                if (Float.isNaN(value[i])) {
+                    context.listensTo(Utils.idFromNan(value[i]), this);
+                }
+            }
+        }
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "BitmapData";
+        }
+
+        @Override
+        public int id() {
+            return Operations.DATA_SHADER;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param shaderID
+         * @param shaderTextId
+         * @param floatMap
+         * @param intMap
+         * @param bitmapMap
+         */
+        public void apply(WireBuffer buffer, int shaderID, int shaderTextId,
+                          HashMap<String, float[]> floatMap,
+                          HashMap<String, int[]> intMap,
+                          HashMap<String, Integer> bitmapMap) {
+            buffer.start(Operations.DATA_SHADER);
+            buffer.writeInt(shaderID);
+
+            buffer.writeInt(shaderTextId);
+            int floatSize = (floatMap == null) ? 0 : floatMap.size();
+            int intSize = (intMap == null) ? 0 : intMap.size();
+            int bitmapSize = (bitmapMap == null) ? 0 : bitmapMap.size();
+            int sizes = floatSize | (intSize << 8) | (bitmapSize << 16);
+            buffer.writeInt(sizes);
+
+            if (floatSize > 0) {
+
+                for (String name : floatMap.keySet()) {
+                    buffer.writeUTF8(name);
+                    float[] values = floatMap.get(name);
+                    buffer.writeInt(values.length);
+
+                    for (int i = 0; i < values.length; i++) {
+                        buffer.writeFloat(values[i]);
+                    }
+                }
+            }
+
+            if (intSize > 0) {
+                for (String name : intMap.keySet()) {
+                    buffer.writeUTF8(name);
+                    int[] values = intMap.get(name);
+                    buffer.writeInt(values.length);
+                    for (int i = 0; i < values.length; i++) {
+                        buffer.writeInt(values[i]);
+                    }
+                }
+            }
+            if (bitmapSize > 0) {
+                for (String name : bitmapMap.keySet()) {
+                    buffer.writeUTF8(name);
+                    int value = bitmapMap.get(name);
+                    buffer.writeInt(value);
+                }
+            }
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int shaderID = buffer.readInt();
+            int shaderTextId = buffer.readInt();
+            HashMap<String, float[]> floatMap = null;
+            HashMap<String, int[]> intMap = null;
+            HashMap<String, Integer> bitmapMap = null;
+
+            int sizes = buffer.readInt();
+
+            int floatMapSize = sizes & 0xFF;
+            if (floatMapSize > 0) {
+                floatMap = new HashMap<>();
+                for (int i = 0; i < floatMapSize; i++) {
+                    String name = buffer.readUTF8();
+                    int len = buffer.readInt();
+                    float[] val = new float[len];
+
+                    for (int j = 0; j < len; j++) {
+                        val[j] = buffer.readFloat();
+                    }
+
+                    floatMap.put(name, val);
+                }
+            }
+            int intMapSize = (sizes >> 8) & 0xFF;
+
+            if (intMapSize > 0) {
+
+                intMap = new HashMap<>();
+                for (int i = 0; i < intMapSize; i++) {
+                    String name = buffer.readUTF8();
+                    int len = buffer.readInt();
+                    int[] val = new int[len];
+                    for (int j = 0; j < len; j++) {
+                        val[j] = buffer.readInt();
+                    }
+                    intMap.put(name, val);
+                }
+            }
+            int bitmapMapSize = (sizes >> 16) & 0xFF;
+
+            if (bitmapMapSize > 0) {
+                bitmapMap = new HashMap<>();
+                for (int i = 0; i < bitmapMapSize; i++) {
+                    String name = buffer.readUTF8();
+                    int val = buffer.readInt();
+                    bitmapMap.put(name, val);
+                }
+            }
+            operations.add(new ShaderData(shaderID, shaderTextId,
+                    floatMap, intMap, bitmapMap));
+        }
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        context.loadShader(mShaderID, this);
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index 5b622ae..ed13449 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -44,11 +44,13 @@
 
     @Override
     public String toString() {
-        return "TEXT DATA " + mTextId + "\"" + mText + "\"";
+        return "TextData[" + mTextId + "] = \""
+                + Utils.trimString(mText, 10) + "\"";
     }
 
     public static class Companion implements CompanionOperation {
-        private Companion() {}
+        private Companion() {
+        }
 
         @Override
         public String name() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
new file mode 100644
index 0000000..65a39a1e
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
@@ -0,0 +1,172 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils;
+
+import java.util.List;
+
+/**
+ * Operation convert floats to text
+ * This command is structured [command][textID][before,after][flags]
+ * before and after define number of digits before and after the decimal point
+ */
+public class TextFromFloat implements Operation, VariableSupport {
+    public int mTextId;
+    public float mValue;
+    public float mOutValue;
+    public short mDigitsBefore;
+    public short mDigitsAfter;
+    public int mFlags;
+    public static final Companion COMPANION = new Companion();
+    public static final int MAX_STRING_SIZE = 4000;
+    char mPre = ' ';
+    char mAfter = ' ';
+    // Theses flags define what how to/if  fill the space
+    public static final int PAD_AFTER_SPACE = 0; // pad past point with space
+    public static final int PAD_AFTER_NONE = 1; // do not pad past last digit
+    public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit
+    public static final int PAD_PRE_SPACE = 0;  // pad before number with spaces
+    public static final int PAD_PRE_NONE = 4;   // pad before number with 0s
+    public static final int PAD_PRE_ZERO = 12;  // do not pad before number
+
+    public TextFromFloat(int textId, float value, short digitsBefore,
+                         short digitsAfter, int flags) {
+        this.mTextId = textId;
+        this.mValue = value;
+        this.mDigitsAfter = digitsAfter;
+        this.mDigitsBefore = digitsBefore;
+        this.mFlags = flags;
+        mOutValue = mValue;
+        switch (mFlags & 3) {
+            case PAD_AFTER_SPACE:
+                mAfter = ' ';
+                break;
+            case PAD_AFTER_NONE:
+                mAfter = 0;
+                break;
+            case PAD_AFTER_ZERO:
+                mAfter = '0';
+                break;
+        }
+        switch (mFlags & 12) {
+            case PAD_PRE_SPACE:
+                mPre = ' ';
+                break;
+            case PAD_PRE_NONE:
+                mPre = 0;
+                break;
+            case PAD_PRE_ZERO:
+                mPre = '0';
+                break;
+        }
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags);
+    }
+
+    @Override
+    public String toString() {
+        return "TextFromFloat[" + mTextId + "] = "
+                + Utils.floatToString(mValue) + " " + mDigitsBefore
+                + "." + mDigitsAfter + " " + mFlags;
+    }
+
+
+    @Override
+    public void updateVariables(RemoteContext context) {
+        if (Float.isNaN(mValue)) {
+            mOutValue = context.getFloat(Utils.idFromNan(mValue));
+        }
+
+    }
+
+
+    @Override
+    public void registerListening(RemoteContext context) {
+        if (Float.isNaN(mValue)) {
+            context.listensTo(Utils.idFromNan(mValue), this);
+        }
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "TextData";
+        }
+
+        @Override
+        public int id() {
+            return Operations.TEXT_FROM_FLOAT;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param textId
+         * @param value
+         * @param digitsBefore
+         * @param digitsAfter
+         * @param flags
+         */
+        public void apply(WireBuffer buffer, int textId,
+                          float value, short digitsBefore,
+                          short digitsAfter, int flags) {
+            buffer.start(Operations.TEXT_FROM_FLOAT);
+            buffer.writeInt(textId);
+            buffer.writeFloat(value);
+            buffer.writeInt((digitsBefore << 16) | digitsAfter);
+            buffer.writeInt(flags);
+
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int textId = buffer.readInt();
+            float value = buffer.readFloat();
+            int tmp = buffer.readInt();
+            short post = (short) (tmp & 0xFFFF);
+            short pre = (short) ((tmp >> 16) & 0xFFFF);
+
+            int flags = buffer.readInt();
+            operations.add(new TextFromFloat(textId, value, pre, post, flags));
+        }
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        float v = mOutValue;
+        String s = StringUtils.floatToString(v, mDigitsBefore,
+                mDigitsAfter, mPre, mAfter);
+        context.loadText(mTextId, s);
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
new file mode 100644
index 0000000..a0fc854
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -0,0 +1,101 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.CompanionOperation;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+
+import java.util.List;
+
+/**
+ * Operation to deal with Text data
+ */
+public class TextMerge implements Operation {
+    public int mTextId;
+    public int mSrcId1;
+    public int mSrcId2;
+    public static final Companion COMPANION = new Companion();
+    public static final int MAX_STRING_SIZE = 4000;
+
+    public TextMerge(int textId, int srcId1, int srcId2) {
+        this.mTextId = textId;
+        this.mSrcId1 = srcId1;
+        this.mSrcId2 = srcId2;
+    }
+
+    @Override
+    public void write(WireBuffer buffer) {
+        COMPANION.apply(buffer, mTextId, mSrcId1, mSrcId2);
+    }
+
+    @Override
+    public String toString() {
+        return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]";
+    }
+
+    public static class Companion implements CompanionOperation {
+        private Companion() {
+        }
+
+        @Override
+        public String name() {
+            return "TextData";
+        }
+
+        @Override
+        public int id() {
+            return Operations.TEXT_MERGE;
+        }
+
+        /**
+         * Writes out the operation to the buffer
+         * @param buffer
+         * @param textId
+         * @param srcId1
+         * @param srcId2
+         */
+        public void apply(WireBuffer buffer, int textId, int srcId1, int srcId2) {
+            buffer.start(Operations.TEXT_MERGE);
+            buffer.writeInt(textId);
+            buffer.writeInt(srcId1);
+            buffer.writeInt(srcId2);
+        }
+
+        @Override
+        public void read(WireBuffer buffer, List<Operation> operations) {
+            int textId = buffer.readInt();
+            int srcId1 = buffer.readInt();
+            int srcId2 = buffer.readInt();
+
+            operations.add(new TextMerge(textId, srcId1, srcId2));
+        }
+    }
+
+    @Override
+    public void apply(RemoteContext context) {
+        String str1 = context.getText(mSrcId1);
+        String str2 = context.getText(mSrcId2);
+        context.loadText(mTextId, str1 + str2);
+    }
+
+    @Override
+    public String deepToString(String indent) {
+        return indent + toString();
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index 00e2f20..fdc6860 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -15,13 +15,16 @@
  */
 package com.android.internal.widget.remotecompose.core.operations;
 
+/**
+ * Utilities to be used across all core operations
+ */
 public class Utils {
     public static float asNan(int v) {
         return Float.intBitsToFloat(v | -0x800000);
     }
 
     public static int idFromNan(float value) {
-        int b =  Float.floatToRawIntBits(value);
+        int b = Float.floatToRawIntBits(value);
         return b & 0xFFFFF;
     }
 
@@ -29,13 +32,194 @@
         return 0;
     }
 
-    String getFloatString(float value) {
-        if (Float.isNaN(value)) {
-            int id = idFromNan(value);
-            if (id > 0) {
-                return "NaN(" + id + ")";
-            }
+    /**
+     * trim a string to n characters if needing to trim
+     * end in "..."
+     *
+     * @param str
+     * @param n
+     * @return
+     */
+    static String trimString(String str, int n) {
+        if (str.length() > n) {
+            str = str.substring(0, n - 3) + "...";
         }
-        return "" + value;
+        return str;
     }
+
+    /**
+     * print the id and the value of a float
+     * @param idvalue
+     * @param value
+     * @return
+     */
+    public static String floatToString(float idvalue, float value) {
+        if (Float.isNaN(idvalue)) {
+            return "[" + idFromNan(idvalue) + "]" + floatToString(value);
+        }
+        return floatToString(value);
+    }
+
+    /**
+     * Convert float to string but render nan id in brackets [n]
+     * @param value
+     * @return
+     */
+    public static String floatToString(float value) {
+        if (Float.isNaN(value)) {
+            return "[" + idFromNan(value) + "]";
+        }
+        return Float.toString(value);
+    }
+
+    /**
+     * Debugging util to print a message and include the file/line it came from
+     * @param str
+     */
+    public static void log(String str) {
+        StackTraceElement s = new Throwable().getStackTrace()[1];
+        System.out.println("(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str);
+    }
+
+    /**
+     * Debugging util to print the stack
+     * @param str
+     * @param n
+     */
+    public static void logStack(String str, int n) {
+        StackTraceElement[] st = new Throwable().getStackTrace();
+        for (int i = 1; i < n + 1; i++) {
+            StackTraceElement s = st[i];
+            String space = new String(new char[i]).replace('\0', ' ');
+            System.out.println(space + "(" + s.getFileName()
+                    + ":" + s.getLineNumber() + ")." + str);
+        }
+    }
+
+    /**
+     * Is a variable Allowed int calculation and references.
+     *
+     * @param v
+     * @return
+     */
+    public static boolean isVariable(float v) {
+        if (Float.isNaN(v)) {
+            int id = idFromNan(v);
+            return id > 40 || id < 10;
+        }
+        return false;
+    }
+
+    /**
+     * print a color in the familiar 0xAARRGGBB pattern
+     *
+     * @param color
+     * @return
+     */
+    public static String colorInt(int color) {
+        String str = "000000000000" + Integer.toHexString(color);
+        return "0x" + str.substring(str.length() - 8);
+    }
+
+    /**
+     * Interpolate two colors.
+     * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t
+     *
+     * @param c1
+     * @param c2
+     * @param t
+     * @return
+     */
+    public static int interpolateColor(int c1, int c2, float t) {
+        if (Float.isNaN(t) || t == 0.0f) {
+            return c1;
+        } else if (t == 1.0f) {
+            return c2;
+        }
+        int a = 0xFF & (c1 >> 24);
+        int r = 0xFF & (c1 >> 16);
+        int g = 0xFF & (c1 >> 8);
+        int b = 0xFF & c1;
+        float f_r = (float) Math.pow(r / 255.0f, 2.2);
+        float f_g = (float) Math.pow(g / 255.0f, 2.2);
+        float f_b = (float) Math.pow(b / 255.0f, 2.2);
+        float c1fr = f_r;
+        float c1fg = f_g;
+        float c1fb = f_b;
+        float c1fa = a / 255f;
+
+        a = 0xFF & (c2 >> 24);
+        r = 0xFF & (c2 >> 16);
+        g = 0xFF & (c2 >> 8);
+        b = 0xFF & c2;
+        f_r = (float) Math.pow(r / 255.0f, 2.2);
+        f_g = (float) Math.pow(g / 255.0f, 2.2);
+        f_b = (float) Math.pow(b / 255.0f, 2.2);
+        float c2fr = f_r;
+        float c2fg = f_g;
+        float c2fb = f_b;
+        float c2fa = a / 255f;
+        f_r = c1fr + t * (c2fr - c1fr);
+        f_g = c1fg + t * (c2fg - c1fg);
+        f_b = c1fb + t * (c2fb - c1fb);
+        float f_a = c1fa + t * (c2fa - c1fa);
+
+        int outr = clamp((int) ((float) Math.pow(f_r, 1.0 / 2.2) * 255.0f));
+        int outg = clamp((int) ((float) Math.pow(f_g, 1.0 / 2.2) * 255.0f));
+        int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f));
+        int outa = clamp((int) (f_a * 255.0f));
+
+
+        return (outa << 24 | outr << 16 | outg << 8 | outb);
+    }
+
+    /**
+     * Efficient clamping function
+     *
+     * @param c
+     * @return number between 0 and 255
+     */
+    public static int clamp(int c) {
+        int n = 255;
+        c &= ~(c >> 31);
+        c -= n;
+        c &= (c >> 31);
+        c += n;
+        return c;
+    }
+
+    /**
+     * convert hue saturation and value to RGB
+     *
+     * @param hue        0..1
+     * @param saturation 0..1 0=on the gray scale
+     * @param value      0..1 0=black
+     * @return
+     */
+    public static int hsvToRgb(float hue, float saturation, float value) {
+        int h = (int) (hue * 6);
+        float f = hue * 6 - h;
+        int p = (int) (0.5f + 255 * value * (1 - saturation));
+        int q = (int) (0.5f + 255 * value * (1 - f * saturation));
+        int t = (int) (0.5f + 255 * value * (1 - (1 - f) * saturation));
+        int v = (int) (0.5f + 255 * value);
+        switch (h) {
+            case 0:
+                return 0XFF000000 | (v << 16) + (t << 8) + p;
+            case 1:
+                return 0XFF000000 | (q << 16) + (v << 8) + p;
+            case 2:
+                return 0XFF000000 | (p << 16) + (v << 8) + t;
+            case 3:
+                return 0XFF000000 | (p << 16) + (q << 8) + v;
+            case 4:
+                return 0XFF000000 | (t << 16) + (p << 8) + v;
+            case 5:
+                return 0XFF000000 | (v << 16) + (p << 8) + q;
+
+        }
+        return 0;
+    }
+
+
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 8abb0bf..a7d0ac6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -15,43 +15,60 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.paint;
 
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
 import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
 
 import java.util.Arrays;
 
+/**
+ * Paint Bundle represents a delta of changes to a paint object
+ */
 public class PaintBundle {
     int[] mArray = new int[200];
+    int[] mOutArray = null;
     int mPos = 0;
 
-    public void applyPaintChange(PaintChanges p) {
+    /**
+     * Apply changes to a PaintChanges interface
+     * @param paintContext
+     * @param p
+     */
+    public void applyPaintChange(PaintContext paintContext, PaintChanges p) {
         int i = 0;
         int mask = 0;
+        if (mOutArray == null) {
+            mOutArray = mArray;
+        }
         while (i < mPos) {
-            int cmd = mArray[i++];
+            int cmd = mOutArray[i++];
             mask = mask | (1 << (cmd - 1));
             switch (cmd & 0xFFFF) {
                 case TEXT_SIZE: {
-                    p.setTextSize(Float.intBitsToFloat(mArray[i++]));
+                    p.setTextSize(Float.intBitsToFloat(mOutArray[i++]));
                     break;
                 }
                 case TYPEFACE:
                     int style = (cmd >> 16);
                     int weight = style & 0x3ff;
                     boolean italic = (style >> 10) > 0;
-                    int font_type = mArray[i++];
+                    int font_type = mOutArray[i++];
 
                     p.setTypeFace(font_type, weight, italic);
                     break;
+                case COLOR_ID: // mOutArray should have already decoded it
                 case COLOR: {
-                    p.setColor(mArray[i++]);
+                    p.setColor(mOutArray[i++]);
                     break;
                 }
                 case STROKE_WIDTH: {
-                    p.setStrokeWidth(Float.intBitsToFloat(mArray[i++]));
+                    p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++]));
                     break;
                 }
                 case STROKE_MITER: {
-                    p.setStrokeMiter(Float.intBitsToFloat(mArray[i++]));
+                    p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++]));
                     break;
                 }
                 case STROKE_CAP: {
@@ -63,6 +80,7 @@
                     break;
                 }
                 case SHADER: {
+                    p.setShader(mOutArray[i++]);
                     break;
                 }
                 case STROKE_JOIN: {
@@ -81,17 +99,16 @@
                     p.setFilterBitmap(!((cmd >> 16) == 0));
                     break;
                 }
-
                 case GRADIENT: {
-                    i = callSetGradient(cmd, mArray, i, p);
+                    i = callSetGradient(cmd, mOutArray, i, p);
                     break;
                 }
                 case COLOR_FILTER: {
-                    p.setColorFilter(mArray[i++], cmd >> 16);
+                    p.setColorFilter(mOutArray[i++], cmd >> 16);
                     break;
                 }
                 case ALPHA: {
-                    p.setAlpha(Float.intBitsToFloat(mArray[i++]));
+                    p.setAlpha(Float.intBitsToFloat(mOutArray[i++]));
                     break;
                 }
             }
@@ -106,7 +123,6 @@
         switch (id) {
             case TEXT_SIZE:
                 return "TEXT_SIZE";
-
             case COLOR:
                 return "COLOR";
             case STROKE_WIDTH:
@@ -133,7 +149,6 @@
                 return "ALPHA";
             case COLOR_FILTER:
                 return "COLOR_FILTER";
-
         }
         return "????" + id + "????";
     }
@@ -154,6 +169,14 @@
         return str + "]";
     }
 
+    private static String asFloatStr(int value) {
+        float fValue = Float.intBitsToFloat(value);
+        if (Float.isNaN(fValue)) {
+            return "[" + Utils.idFromNan(fValue) + "]";
+        }
+        return Float.toString(fValue);
+    }
+
     @Override
     public String toString() {
         StringBuilder ret = new StringBuilder("\n");
@@ -164,7 +187,8 @@
             switch (type) {
 
                 case TEXT_SIZE: {
-                    ret.append("    TextSize(" + Float.intBitsToFloat(mArray[i++]));
+                    ret.append("    TextSize("
+                            + asFloatStr(mArray[i++]));
                 }
 
                 break;
@@ -181,14 +205,18 @@
                     ret.append("    Color(" + colorInt(mArray[i++]));
                 }
                 break;
+                case COLOR_ID: {
+                    ret.append("    ColorId([" + mArray[i++] + "]");
+                }
+                break;
                 case STROKE_WIDTH: {
                     ret.append("    StrokeWidth("
-                            + (Float.intBitsToFloat(mArray[i++])));
+                            + (asFloatStr(mArray[i++])));
                 }
                 break;
                 case STROKE_MITER: {
                     ret.append("    StrokeMiter("
-                            + (Float.intBitsToFloat(mArray[i++])));
+                            + (asFloatStr(mArray[i++])));
                 }
                 break;
                 case STROKE_CAP: {
@@ -207,11 +235,12 @@
                 }
                 break;
                 case SHADER: {
+                    ret.append("    Shader(" + mArray[i++]);
                 }
                 break;
                 case ALPHA: {
                     ret.append("    Alpha("
-                            + (Float.intBitsToFloat(mArray[i++])));
+                            + (asFloatStr(mArray[i++])));
                 }
                 break;
                 case IMAGE_FILTER_QUALITY: {
@@ -244,7 +273,6 @@
         return ret.toString();
     }
 
-
     int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) {
         int ret = i;
         int type = (cmd >> 16);
@@ -258,26 +286,25 @@
                     colors = new int[len];
                     for (int j = 0; j < colors.length; j++) {
                         colors[j] = array[ret++];
-
                     }
                 }
                 len = array[ret++];
-                float[] stops = null;
+                String[] stops = null;
                 if (len > 0) {
-                    stops = new float[len];
+                    stops = new String[len];
                     for (int j = 0; j < stops.length; j++) {
-                        stops[j] = Float.intBitsToFloat(array[ret++]);
+                        stops[j] = asFloatStr(array[ret++]);
                     }
                 }
 
                 p.append("      colors = " + colorInt(colors) + ",\n");
                 p.append("      stops = " + Arrays.toString(stops) + ",\n");
                 p.append("      start = ");
-                p.append("[" + Float.intBitsToFloat(array[ret++]));
-                p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+                p.append("[" + asFloatStr(array[ret++]));
+                p.append(", " + asFloatStr(array[ret++]) + "],\n");
                 p.append("      end = ");
-                p.append("[" + Float.intBitsToFloat(array[ret++]));
-                p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+                p.append("[" + asFloatStr(array[ret++]));
+                p.append(", " + asFloatStr(array[ret++]) + "],\n");
                 int tileMode = array[ret++];
                 p.append("      tileMode = " + tileMode + "\n    ");
             }
@@ -295,21 +322,21 @@
                     }
                 }
                 len = array[ret++];
-                float[] stops = null;
+                String[] stops = null;
                 if (len > 0) {
-                    stops = new float[len];
+                    stops = new String[len];
                     for (int j = 0; j < stops.length; j++) {
-                        stops[j] = Float.intBitsToFloat(array[ret++]);
+                        stops[j] = asFloatStr(array[ret++]);
                     }
                 }
 
                 p.append("      colors = " + colorInt(colors) + ",\n");
                 p.append("      stops = " + Arrays.toString(stops) + ",\n");
                 p.append("      center = ");
-                p.append("[" + Float.intBitsToFloat(array[ret++]));
-                p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n");
+                p.append("[" + asFloatStr(array[ret++]));
+                p.append(", " + asFloatStr(array[ret++]) + "],\n");
                 p.append("      radius =");
-                p.append(" " + Float.intBitsToFloat(array[ret++]) + ",\n");
+                p.append(" " + asFloatStr(array[ret++]) + ",\n");
                 int tileMode = array[ret++];
                 p.append("      tileMode = " + tileMode + "\n    ");
             }
@@ -327,20 +354,19 @@
                     }
                 }
                 len = array[ret++];
-                float[] stops = null;
+                String[] stops = null;
                 if (len > 0) {
-                    stops = new float[len];
+                    stops = new String[len];
                     for (int j = 0; j < stops.length; j++) {
-                        stops[j] = Float.intBitsToFloat(array[ret++]);
+                        stops[j] = asFloatStr(array[ret++]);
                     }
                 }
-
                 p.append("      colors = " + colorInt(colors) + ",\n");
                 p.append("      stops = " + Arrays.toString(stops) + ",\n");
                 p.append("      center = ");
-                p.append("[" + Float.intBitsToFloat(array[ret++]));
-                p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n    ");
-
+                p.append("[" + asFloatStr(array[ret++]));
+                p.append(", "
+                        + asFloatStr(array[ret++]) + "],\n    ");
             }
             break;
             default: {
@@ -376,7 +402,6 @@
             return ret;
         }
 
-
         switch (gradientType) {
 
             case LINEAR_GRADIENT: {
@@ -433,7 +458,7 @@
     public static final int COLOR = 4;  // int
     public static final int STROKE_WIDTH = 5; // float
     public static final int STROKE_MITER = 6;
-    public static final int STROKE_CAP = 7; // int
+    public static final int STROKE_CAP = 7; //  int
     public static final int STYLE = 8; // int
     public static final int SHADER = 9; // int
     public static final int IMAGE_FILTER_QUALITY = 10; // int
@@ -445,7 +470,7 @@
     public static final int TYPEFACE = 16;
     public static final int FILTER_BITMAP = 17;
     public static final int BLEND_MODE = 18;
-
+    public static final int COLOR_ID = 19;  // int
 
     public static final int BLEND_MODE_CLEAR = 0;
     public static final int BLEND_MODE_SRC = 1;
@@ -634,8 +659,8 @@
 
     /**
      * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
-     * @param weight    100-1000
-     * @param italic    tur
+     * @param weight   100-1000
+     * @param italic   tur
      */
     public void setTextStyle(int fontType, int weight, boolean italic) {
         int style = (weight & 0x3FF) | (italic ? 2048 : 0);  // pack the weight and italic
@@ -658,6 +683,10 @@
         mPos++;
     }
 
+    /**
+     * Set the Color based on Color
+     * @param color
+     */
     public void setColor(int color) {
         mArray[mPos] = COLOR;
         mPos++;
@@ -666,6 +695,18 @@
     }
 
     /**
+     * Set the Color based on ID
+     * @param color
+     */
+    public void setColorId(int color) {
+        mArray[mPos] = COLOR_ID;
+        mPos++;
+        mArray[mPos] = color;
+        mPos++;
+    }
+
+
+    /**
      * Set the paint's Cap.
      *
      * @param cap set the paint's line cap style, used whenever the paint's
@@ -676,16 +717,29 @@
         mPos++;
     }
 
+    /**
+     * Set the style STROKE and/or FILL
+     * @param style
+     */
     public void setStyle(int style) {
         mArray[mPos] = STYLE | (style << 16);
         mPos++;
     }
 
-    public void setShader(int shader, String shaderString) {
-        mArray[mPos] = SHADER | (shader << 16);
+    /**
+     * Set the shader id to use
+     * @param shaderId
+     */
+    public void setShader(int shaderId) {
+        mArray[mPos] = SHADER;
+        mPos++;
+        mArray[mPos] = shaderId;
         mPos++;
     }
 
+    /**
+     * Set the Alpha value
+     */
     public void setAlpha(float alpha) {
         mArray[mPos] = ALPHA;
         mPos++;
@@ -729,7 +783,6 @@
      * destination pixels
      * (content of the render target).
      *
-     *
      * @param blendmode The blend mode to be installed in the paint
      */
     public void setBlendMode(int blendmode) {
@@ -825,5 +878,216 @@
         return "null";
     }
 
-}
+    /**
+     * Check all the floats for Nan(id) floats and call listenTo
+     * @param context
+     * @param support
+     */
+    public void registerVars(RemoteContext context, VariableSupport support) {
+        int i = 0;
+        while (i < mPos) {
+            int cmd = mArray[i++];
+            int type = cmd & 0xFFFF;
+            switch (type) {
+                case STROKE_MITER:
+                case STROKE_WIDTH:
+                case ALPHA:
+                case TEXT_SIZE:
+                    float v = Float.intBitsToFloat(mArray[i++]);
+                    if (Float.isNaN(v)) {
+                        context.listensTo(Utils.idFromNan(v), support);
+                    }
+                    break;
+                case COLOR_ID:
+                    context.listensTo(mArray[i++], support);
+                    break;
+                case COLOR:
 
+                case TYPEFACE:
+                case SHADER:
+                case COLOR_FILTER:
+                    i++;
+                    break;
+                case STROKE_JOIN:
+                case FILTER_BITMAP:
+                case STROKE_CAP:
+                case STYLE:
+                case IMAGE_FILTER_QUALITY:
+                case BLEND_MODE:
+                case ANTI_ALIAS:
+                    break;
+
+                case GRADIENT: {
+                    // TODO gradients should be handled correctly
+                    i = callPrintGradient(cmd, mArray, i, new StringBuilder());
+                }
+            }
+        }
+    }
+
+    /**
+     * Update variables if any are float ids
+     * @param context
+     */
+    public void updateVariables(RemoteContext context) {
+        if (mOutArray == null) {
+            mOutArray = Arrays.copyOf(mArray, mArray.length);
+        } else {
+            System.arraycopy(mArray, 0, mOutArray, 0, mArray.length);
+        }
+        int i = 0;
+        while (i < mPos) {
+            int cmd = mArray[i++];
+            int type = cmd & 0xFFFF;
+            switch (type) {
+                case STROKE_MITER:
+                case STROKE_WIDTH:
+                case ALPHA:
+                case TEXT_SIZE:
+                    mOutArray[i] = fixFloatVar(mArray[i], context);
+                    i++;
+                    break;
+                case COLOR_ID:
+                    mOutArray[i] = fixColor(mArray[i], context);
+                    i++;
+                    break;
+                case COLOR:
+                case TYPEFACE:
+                case SHADER:
+                case COLOR_FILTER:
+                    i++;
+                    break;
+                case STROKE_JOIN:
+                case FILTER_BITMAP:
+                case STROKE_CAP:
+                case STYLE:
+                case IMAGE_FILTER_QUALITY:
+                case BLEND_MODE:
+                case ANTI_ALIAS:
+                    break;
+
+                case GRADIENT: {
+                    // TODO gradients should be handled correctly
+                    i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context);
+                }
+            }
+        }
+    }
+
+    private int fixFloatVar(int val, RemoteContext context) {
+        float v = Float.intBitsToFloat(val);
+        if (Float.isNaN(v)) {
+            int id = Utils.idFromNan(v);
+            return Float.floatToRawIntBits(context.getFloat(id));
+        }
+        return val;
+    }
+
+    private int fixColor(int colorId, RemoteContext context) {
+        int n = context.getColor(colorId);
+        return n;
+    }
+
+    int updateFloatsInGradient(int cmd, int[] out, int[] array,
+                               int i,
+                               RemoteContext context) {
+        int ret = i;
+        int type = (cmd >> 16);
+        switch (type) {
+            case 0: {
+                int len = array[ret++];
+                if (len > 0) {
+                    for (int j = 0; j < len; j++) {
+                        ret++;
+                    }
+                }
+                len = array[ret++];
+
+                if (len > 0) {
+                    for (int j = 0; j < len; j++) {
+                        out[ret] = fixFloatVar(array[ret], context);
+                        ret++;
+                    }
+                }
+
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+
+                //      end
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+                ret++; // tileMode
+            }
+
+            break;
+            case 1: {
+                //   RadialGradient
+                int len = array[ret++];
+                if (len > 0) {
+                    for (int j = 0; j < len; j++) {
+                        ret++;
+                    }
+                }
+                len = array[ret++];
+                if (len > 0) {
+                    for (int j = 0; j < len; j++) {
+                        out[ret] = fixFloatVar(array[ret], context);
+                        ret++;
+                    }
+                }
+
+
+                //    center
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+                //     radius
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+                ret++; // tileMode
+
+            }
+
+            break;
+            case 2: {
+                //   SweepGradient
+                int len = array[ret++];
+                int[] colors = null;
+                if (len > 0) {
+                    colors = new int[len];
+                    for (int j = 0; j < colors.length; j++) {
+                        colors[j] = array[ret++];
+
+                    }
+                }
+                len = array[ret++];
+                float[] stops = null;
+                if (len > 0) {
+                    stops = new float[len];
+                    for (int j = 0; j < stops.length; j++) {
+                        out[ret] = fixFloatVar(array[ret], context);
+                        ret++;
+                    }
+                }
+
+                //      center
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+                out[ret] = fixFloatVar(array[ret], context);
+                ret++;
+            }
+            break;
+            default: {
+                System.err.println("gradient type unknown");
+            }
+        }
+
+        return ret;
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
index 994bf6d..28fe63a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
@@ -27,7 +27,6 @@
 
     }
 
-
     @Override
     public void setStrokeWidth(float width) {
 
@@ -49,7 +48,7 @@
     }
 
     @Override
-    public void setShader(int shader, String shaderString) {
+    public void setShader(int shader) {
 
     }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
index 87e58ac..d5dc388 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
@@ -15,9 +15,14 @@
  */
 package com.android.internal.widget.remotecompose.core.operations.paint;
 
+/**
+ * Interface to a paint object
+ * For more details see Android Paint
+ */
 public interface PaintChanges {
 
-
+    // MASK to be set/cleared
+    int CLEAR_TEXT_SIZE = 1 << (PaintBundle.TEXT_SIZE - 1);
     int CLEAR_TEXT_STYLE = 1 << (PaintBundle.TYPEFACE - 1);
     int CLEAR_COLOR = 1 << (PaintBundle.COLOR - 1);
     int CLEAR_STROKE_WIDTH = 1 << (PaintBundle.STROKE_WIDTH - 1);
@@ -32,21 +37,101 @@
     int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1);
     int VALID_BITS = 0x1FFF; // only the first 13 bit are valid now
 
-
+    /**
+     * Set the size of text
+     * @param size
+     */
     void setTextSize(float size);
+
+    /**
+     * Set the width of lines
+     * @param width
+     */
     void setStrokeWidth(float width);
+
+    /**
+     * Set the color to use
+     * @param color
+     */
     void setColor(int color);
+
+    /**
+     * Set the Stroke Cap
+     * @param cap
+     */
     void setStrokeCap(int cap);
+
+    /**
+     * Set the Stroke style FILL and/or STROKE
+     * @param style
+     */
     void setStyle(int style);
-    void setShader(int shader, String shaderString);
+
+    /**
+     * Set the id of the shader to use
+     * @param shader
+     */
+    void setShader(int shader);
+
+    /**
+     * Set the way image is interpolated
+     * @param quality
+     */
     void setImageFilterQuality(int quality);
+
+    /**
+     * Set the alpha to draw under
+     * @param a
+     */
     void setAlpha(float a);
+
+    /**
+     * Set the Stroke Miter
+     * @param miter
+     */
     void setStrokeMiter(float miter);
+
+    /**
+     * Set the Stroke Join
+     * @param join
+     */
     void setStrokeJoin(int join);
+
+    /**
+     * Should bitmaps be interpolated
+     * @param filter
+     */
     void setFilterBitmap(boolean filter);
+
+    /**
+     * Set the blend mode can be porterduff + others
+     * @param mode
+     */
     void setBlendMode(int mode);
+
+    /**
+     * Set the AntiAlias. Typically true
+     * Set to off when you need pixilated look (e.g. QR codes)
+     * @param aa
+     */
     void setAntiAlias(boolean aa);
+
+    /**
+     * Clear some sub set of the settings
+     * @param mask
+     */
     void clear(long mask);
+
+    /**
+     * Set a linear gradient fill
+     * @param colorsArray
+     * @param stopsArray
+     * @param startX
+     * @param startY
+     * @param endX
+     * @param endY
+     * @param tileMode
+     */
     void setLinearGradient(
             int[] colorsArray,
             float[] stopsArray,
@@ -57,6 +142,15 @@
             int tileMode
     );
 
+    /**
+     * Set a radial gradient fill
+     * @param colorsArray
+     * @param stopsArray
+     * @param centerX
+     * @param centerY
+     * @param radius
+     * @param tileMode
+     */
     void setRadialGradient(
             int[] colorsArray,
             float[] stopsArray,
@@ -66,6 +160,13 @@
             int tileMode
     );
 
+    /**
+     * Set a sweep gradient fill
+     * @param colorsArray
+     * @param stopsArray
+     * @param centerX
+     * @param centerY
+     */
     void setSweepGradient(
             int[] colorsArray,
             float[] stopsArray,
@@ -73,9 +174,19 @@
             float centerY
     );
 
-
+    /**
+     * Set Color filter mod
+     * @param color
+     * @param mode
+     */
     void setColorFilter(int color, int mode);
 
+    /**
+     * Set TypeFace 0,1,2
+     * TODO above should point to a string to be decoded
+     * @param fontType
+     * @param weight
+     * @param italic
+     */
     void setTypeFace(int fontType, int weight, boolean italic);
-}
-
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
new file mode 100644
index 0000000..616048d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -0,0 +1,452 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.utilities;
+
+/**
+ * high performance floating point expression evaluator used in animation
+ */
+public class AnimatedFloatExpression {
+    static IntMap<String> sNames = new IntMap<>();
+    public static final int OFFSET = 0x100;
+    public static final float ADD = asNan(OFFSET + 1);
+    public static final float SUB = asNan(OFFSET + 2);
+    public static final float MUL = asNan(OFFSET + 3);
+    public static final float DIV = asNan(OFFSET + 4);
+    public static final float MOD = asNan(OFFSET + 5);
+    public static final float MIN = asNan(OFFSET + 6);
+    public static final float MAX = asNan(OFFSET + 7);
+    public static final float POW = asNan(OFFSET + 8);
+    public static final float SQRT = asNan(OFFSET + 9);
+    public static final float ABS = asNan(OFFSET + 10);
+    public static final float SIGN = asNan(OFFSET + 11);
+    public static final float COPY_SIGN = asNan(OFFSET + 12);
+    public static final float EXP = asNan(OFFSET + 13);
+    public static final float FLOOR = asNan(OFFSET + 14);
+    public static final float LOG = asNan(OFFSET + 15);
+    public static final float LN = asNan(OFFSET + 16);
+    public static final float ROUND = asNan(OFFSET + 17);
+    public static final float SIN = asNan(OFFSET + 18);
+    public static final float COS = asNan(OFFSET + 19);
+    public static final float TAN = asNan(OFFSET + 20);
+    public static final float ASIN = asNan(OFFSET + 21);
+    public static final float ACOS = asNan(OFFSET + 22);
+
+    public static final float ATAN = asNan(OFFSET + 23);
+
+    public static final float ATAN2 = asNan(OFFSET + 24);
+    public static final float MAD = asNan(OFFSET + 25);
+    public static final float IFELSE = asNan(OFFSET + 26);
+
+    public static final float CLAMP = asNan(OFFSET + 27);
+    public static final float CBRT = asNan(OFFSET + 28);
+    public static final float DEG = asNan(OFFSET + 29);
+    public static final float RAD = asNan(OFFSET + 30);
+    public static final float CEIL = asNan(OFFSET + 31);
+
+
+    public static final float LAST_OP = 31;
+
+
+    public static final float VAR1 = asNan(OFFSET + 27);
+    public static final float VAR2 = asNan(OFFSET + 28);
+
+    // TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR
+    private static final float FP_PI = (float) Math.PI;
+    private static final float FP_TO_RAD = 57.29577951f; // 180/PI
+    private static final float FP_TO_DEG = 0.01745329252f; // 180/PI
+
+    float[] mStack;
+    float[] mLocalStack = new float[128];
+    float[] mVar;
+
+    /**
+     * is float a math operator
+     * @param v
+     * @return
+     */
+    public static boolean isMathOperator(float v) {
+        if (Float.isNaN(v)) {
+            int pos = fromNaN(v);
+            return pos > OFFSET && pos <= OFFSET + LAST_OP;
+        }
+        return false;
+    }
+
+    interface Op {
+        int eval(int sp);
+    }
+
+    /**
+     * Evaluate a float expression
+     * @param exp
+     * @param var
+     * @return
+     */
+    public float eval(float[] exp, float... var) {
+        mStack = exp;
+        mVar = var;
+        int sp = -1;
+        for (int i = 0; i < mStack.length; i++) {
+            float v = mStack[i];
+            if (Float.isNaN(v)) {
+                sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+            } else {
+                mStack[++sp] = v;
+            }
+        }
+        return mStack[sp];
+    }
+
+    /**
+     * Evaluate a float expression
+     * @param exp
+     * @param len
+     * @param var
+     * @return
+     */
+    public float eval(float[] exp, int len, float... var) {
+        System.arraycopy(exp, 0, mLocalStack, 0, len);
+        mStack = mLocalStack;
+        mVar = var;
+        int sp = -1;
+        for (int i = 0; i < len; i++) {
+            float v = mStack[i];
+            if (Float.isNaN(v)) {
+                sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+            } else {
+                mStack[++sp] = v;
+            }
+        }
+        return mStack[sp];
+    }
+
+    /**
+     * Evaluate a float expression
+     * @param exp
+     * @param var
+     * @return
+     */
+    public float evalDB(float[] exp, float... var) {
+        mStack = exp;
+        mVar = var;
+        int sp = -1;
+        for (float v : exp) {
+            if (Float.isNaN(v)) {
+                System.out.print(" " + sNames.get((fromNaN(v) - OFFSET)));
+                sp = mOps[fromNaN(v) - OFFSET].eval(sp);
+            } else {
+                System.out.print(" " + v);
+                mStack[++sp] = v;
+            }
+        }
+        return mStack[sp];
+    }
+
+    Op[] mOps = {
+            null,
+            (sp) -> { // ADD
+                mStack[sp - 1] = mStack[sp - 1] + mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // SUB
+                mStack[sp - 1] = mStack[sp - 1] - mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // MUL
+                mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> {  // DIV
+                mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> {  // MOD
+                mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
+                return sp - 1;
+            },
+            (sp) -> { // MIN
+                mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]);
+                return sp - 1;
+            },
+            (sp) -> { // MAX
+                mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]);
+                return sp - 1;
+            },
+            (sp) -> { // POW
+                mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]);
+                return sp - 1;
+            },
+            (sp) -> { // SQRT
+                mStack[sp] = (float) Math.sqrt(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // ABS
+                mStack[sp] = (float) Math.abs(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // SIGN
+                mStack[sp] = (float) Math.signum(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // copySign
+                mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]);
+                return sp - 1;
+            },
+            (sp) -> { // EXP
+                mStack[sp] = (float) Math.exp(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // FLOOR
+                mStack[sp] = (float) Math.floor(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // LOG
+                mStack[sp] = (float) Math.log10(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // LN
+                mStack[sp] = (float) Math.log(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // ROUND
+                mStack[sp] = (float) Math.round(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // SIN
+                mStack[sp] = (float) Math.sin(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // COS
+                mStack[sp] = (float) Math.cos(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // TAN
+                mStack[sp] = (float) Math.tan(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // ASIN
+                mStack[sp] = (float) Math.asin(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // ACOS
+                mStack[sp] = (float) Math.acos(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // ATAN
+                mStack[sp] = (float) Math.atan(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // ATAN2
+                mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]);
+                return sp - 1;
+            },
+            (sp) -> { // MAD
+                mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
+                return sp - 2;
+            },
+            (sp) -> { // Ternary conditional
+                mStack[sp - 2] = (mStack[sp] > 0)
+                        ? mStack[sp - 1] : mStack[sp - 2];
+                return sp - 2;
+            },
+            (sp) -> { // CLAMP(min,max, val)
+                mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
+                        mStack[sp - 1]);
+                return sp - 2;
+            },
+            (sp) -> { // CBRT cuberoot
+                mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.);
+                return sp;
+            },
+            (sp) -> { // DEG
+                mStack[sp] = mStack[sp] * FP_TO_RAD;
+                return sp;
+            },
+            (sp) -> { // RAD
+                mStack[sp] = mStack[sp] * FP_TO_DEG;
+                return sp;
+            },
+            (sp) -> { // CEIL
+                mStack[sp] = (float) Math.ceil(mStack[sp]);
+                return sp;
+            },
+            (sp) -> { // first var =
+                mStack[sp] = mVar[0];
+                return sp;
+            },
+            (sp) -> { // second var y?
+                mStack[sp] = mVar[1];
+                return sp;
+            },
+            (sp) -> { // 3rd var z?
+                mStack[sp] = mVar[2];
+                return sp;
+            },
+    };
+
+    static {
+        int k = 0;
+        sNames.put(k++, "NOP");
+        sNames.put(k++, "+");
+        sNames.put(k++, "-");
+        sNames.put(k++, "*");
+        sNames.put(k++, "/");
+        sNames.put(k++, "%");
+        sNames.put(k++, "min");
+        sNames.put(k++, "max");
+        sNames.put(k++, "pow");
+        sNames.put(k++, "sqrt");
+        sNames.put(k++, "abs");
+        sNames.put(k++, "sign");
+        sNames.put(k++, "copySign");
+        sNames.put(k++, "exp");
+        sNames.put(k++, "floor");
+        sNames.put(k++, "log");
+        sNames.put(k++, "ln");
+        sNames.put(k++, "round");
+        sNames.put(k++, "sin");
+        sNames.put(k++, "cos");
+        sNames.put(k++, "tan");
+        sNames.put(k++, "asin");
+        sNames.put(k++, "acos");
+        sNames.put(k++, "atan");
+        sNames.put(k++, "atan2");
+        sNames.put(k++, "mad");
+        sNames.put(k++, "ifElse");
+        sNames.put(k++, "clamp");
+        sNames.put(k++, "cbrt");
+        sNames.put(k++, "deg");
+        sNames.put(k++, "rad");
+        sNames.put(k++, "ceil");
+        sNames.put(k++, "a[0]");
+        sNames.put(k++, "a[1]");
+        sNames.put(k++, "a[2]");
+    }
+
+    /**
+     * given a float command return its math name (e.g sin, cos etc.)
+     * @param f
+     * @return
+     */
+    public static String toMathName(float f) {
+        int id = fromNaN(f) - OFFSET;
+        return sNames.get(id);
+    }
+
+    /**
+     * Convert an expression encoded as an array of floats int ot a string
+     * @param exp
+     * @param labels
+     * @return
+     */
+    public static String toString(float[] exp, String[] labels) {
+        StringBuilder s = new StringBuilder();
+        for (int i = 0; i < exp.length; i++) {
+            float v = exp[i];
+            if (Float.isNaN(v)) {
+                if (isMathOperator(v)) {
+                    s.append(toMathName(v));
+                } else {
+                    s.append("[");
+                    s.append(fromNaN(v));
+                    s.append("]");
+                }
+            } else {
+                if (labels[i] != null) {
+                    s.append(labels[i]);
+                }
+                s.append(v);
+            }
+            s.append(" ");
+        }
+        return s.toString();
+    }
+
+    static String toString(float[] exp, int sp) {
+        String[] str = new String[exp.length];
+        if (Float.isNaN(exp[sp])) {
+            int id = fromNaN(exp[sp]) - OFFSET;
+            switch (NO_OF_OPS[id]) {
+                case -1:
+                    return "nop";
+                case 1:
+                    return sNames.get(id) + "(" + toString(exp, sp + 1) + ") ";
+                case 2:
+                    if (infix(id)) {
+                        return "(" + toString(exp, sp + 1)
+                                + sNames.get(id) + " "
+                                + toString(exp, sp + 2) + ") ";
+                    } else {
+                        return sNames.get(id) + "("
+                                + toString(exp, sp + 1) + ", "
+                                + toString(exp, sp + 2) + ")";
+                    }
+                case 3:
+                    if (infix(id)) {
+                        return "((" + toString(exp, sp + 1) + ") ? "
+                                + toString(exp, sp + 2) + ":"
+                                + toString(exp, sp + 3) + ")";
+                    } else {
+                        return sNames.get(id)
+                                + "(" + toString(exp, sp + 1)
+                                + ", " + toString(exp, sp + 2)
+                                + ", " + toString(exp, sp + 3) + ")";
+                    }
+            }
+        }
+        return Float.toString(exp[sp]);
+    }
+
+    static final int[] NO_OF_OPS = {
+            -1, // no op
+            2, 2, 2, 2, 2, // + - * / %
+            2, 2, 2,  // min max, power
+            1, 1, 1, 1, 1, 1, 1, 1,  //sqrt,abs,CopySign,exp,floor,log,ln
+            1, 1, 1, 1, 1, 1, 1, 2,  // round,sin,cos,tan,asin,acos,atan,atan2
+            3, 3, 3, 1, 1, 1, 1,
+            0, 0, 0 // mad, ?:,
+            // a[0],a[1],a[2]
+    };
+
+    /**
+     * to be used by parser to determine if command is infix
+     * @param n
+     * @return
+     */
+    static boolean infix(int n) {
+        return ((n < 6) || (n == 25) || (n == 26));
+    }
+
+    /**
+     * Convert an id into a NaN object
+     * @param v
+     * @return
+     */
+    public static float asNan(int v) {
+        return Float.intBitsToFloat(v | -0x800000);
+    }
+
+    /**
+     * Get ID from a NaN float
+     * @param v
+     * @return
+     */
+    public static int fromNaN(float v) {
+        int b = Float.floatToRawIntBits(v);
+        return b & 0xFFFFF;
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
new file mode 100644
index 0000000..0ea28a8
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+/**
+ * These are tools to use long Color as variables
+ * long colors are stored a 0xXXXXXXXX XXXXXX??
+ * in SRGB the colors are stored 0xAARRGGBB,00000000
+ * SRGB color sapce is color space 0
+ * Our Color will use color float with a
+ * Current android supports
+ * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020,
+ * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES,
+ * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
+ *
+ * Our color space will be 62 (MAX_ID-1). (0x3E)
+ * Storing the default value in SRGB format and having the
+ * id of the color between the ARGB values and the 62 i.e.
+ * 0xAARRGGBB 00 00 00 3E
+ *
+ */
+public class ColorUtils {
+    public static int RC_COLOR = 62;
+
+    long packRCColor(int defaultARGB, int id) {
+        long l = defaultARGB;
+        return (l << 32) | id << 8 | RC_COLOR;
+    }
+
+    boolean isRCColor(long color) {
+        return ((color & 0x3F) == 62);
+    }
+
+    int getID(long color) {
+        if (isRCColor(color)) {
+            return (int) ((color & 0xFFFFFF00) >> 8);
+        }
+        return -1;
+    }
+
+    /**
+     * get default color from long color
+     * @param color
+     * @return
+     */
+    public int getDefaultColor(long color) {
+        if (isRCColor(color)) {
+            return (int) (color >> 32);
+        }
+        if (((color & 0xFF) == 0)) {
+            return (int) (color >> 32);
+        }
+        return 0;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index 8051ef1..0512fa6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -50,7 +50,6 @@
         return insert(key, value);
     }
 
-
     public  T get(int key) {
         int index = findKey(key);
         if (index == -1) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
new file mode 100644
index 0000000..f4cd504
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
@@ -0,0 +1,75 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.utilities;
+
+import com.android.internal.widget.remotecompose.core.operations.Utils;
+
+/**
+ * This defines the major id maps and ranges used by remote compose
+ * Generally ids ranging from 0 ... FFF (4095) are for ids
+ * 0x1000-0x1100 are used for path operations in PathData
+ * 0x1100-0x1200 are used for math operations in Animated float
+ * 0x
+ */
+public class NanMap {
+
+    public static final int MOVE = 0x1000;
+    public static final int LINE = 0x1001;
+    public static final int QUADRATIC = 0x1002;
+    public static final int CONIC = 0x1003;
+    public static final int CUBIC = 0x1004;
+    public static final int CLOSE = 0x1005;
+    public static final int DONE = 0x1006;
+    public static final float MOVE_NAN = Utils.asNan(MOVE);
+    public static final float LINE_NAN = Utils.asNan(LINE);
+    public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC);
+    public static final float CONIC_NAN = Utils.asNan(CONIC);
+    public static final float CUBIC_NAN = Utils.asNan(CUBIC);
+    public static final float CLOSE_NAN = Utils.asNan(CLOSE);
+    public static final float DONE_NAN = Utils.asNan(DONE);
+
+    /**
+     *
+     */
+    public static final float ADD = asNan(0x1100);
+    public static final float SUB = asNan(0x1101);
+    public static final float MUL = asNan(0x1102);
+    public static final float DIV = asNan(0x1103);
+    public static final float MOD = asNan(0x1104);
+    public static final float MIN = asNan(0x1105);
+    public static final float MAX = asNan(0x1106);
+    public static final float POW = asNan(0x1107);
+
+
+    /**
+     * Get ID from Nan float
+     * @param v
+     * @return
+     */
+    public static int fromNaN(float v) {
+        int b = Float.floatToRawIntBits(v);
+        return b & 0xFFFFF;
+    }
+
+    /**
+     * Given id return as a Nan float
+     * @param v
+     * @return
+     */
+    public static float asNan(int v) {
+        return Float.intBitsToFloat(v | 0xFF800000);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
new file mode 100644
index 0000000..8dd5405
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+import java.util.Arrays;
+
+/**
+ * Utilities for string manipulation
+ */
+public class StringUtils {
+    /**
+     * Converts a float into a string.
+     * Providing a defined number of characters before and after the
+     * decimal point.
+     *
+     * @param value              The value to convert to string
+     * @param beforeDecimalPoint digits before the decimal point
+     * @param afterDecimalPoint  digits after the decimal point
+     * @param pre                character to pad width 0 = no pad typically ' ' or '0'
+     * @param post               character to pad width 0 = no pad typically ' ' or '0'
+     * @return
+     */
+    public static String floatToString(float value,
+                                       int beforeDecimalPoint,
+                                       int afterDecimalPoint,
+                                       char pre, char post) {
+
+        int integerPart = (int) value;
+        float fractionalPart = value % 1;
+
+        // Convert integer part to string and pad with spaces
+        String integerPartString = String.valueOf(integerPart);
+        int iLen = integerPartString.length();
+        if (iLen < beforeDecimalPoint) {
+            int spacesToPad = beforeDecimalPoint - iLen;
+            if (pre != 0) {
+                char[] pad = new char[spacesToPad];
+                Arrays.fill(pad, pre);
+                integerPartString = new String(pad) + integerPartString;
+            }
+
+
+        } else if (iLen > beforeDecimalPoint) {
+            integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
+        }
+        if (afterDecimalPoint == 0) {
+            return integerPartString;
+        }
+        // Convert fractional part to string and pad with zeros
+
+        for (int i = 0; i < afterDecimalPoint; i++) {
+            fractionalPart *= 10;
+        }
+
+        fractionalPart = Math.round(fractionalPart);
+
+        for (int i = 0; i < afterDecimalPoint; i++) {
+            fractionalPart *= .1;
+        }
+
+        String fact = Float.toString(fractionalPart);
+        fact = fact.substring(2, Math.min(fact.length(), afterDecimalPoint + 2));
+        int trim = fact.length();
+        for (int i = fact.length() - 1; i >= 0; i--) {
+            if (fact.charAt(i) != '0') {
+                break;
+            }
+            trim--;
+        }
+        if (trim != fact.length()) {
+            fact = fact.substring(0, trim);
+        }
+        int len = fact.length();
+        if (post != 0 && len < afterDecimalPoint) {
+            char[] c = new char[afterDecimalPoint - len];
+            Arrays.fill(c, post);
+            fact = fact + new String(c);
+        }
+
+        return integerPartString + "." + fact;
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
new file mode 100644
index 0000000..c3cd5ae9
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
@@ -0,0 +1,67 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Provide a specific bouncing easing function
+ */
+public class BounceCurve extends Easing {
+    private static final float N1 = 7.5625f;
+    private static final float D1 = 2.75f;
+
+    BounceCurve(int type) {
+        mType = type;
+    }
+
+    @Override
+    public float get(float x) {
+        float t = x;
+        if (t < 0) {
+            return 0f;
+        }
+        if (t < 1 / D1) {
+            return 1 / (1 + 1 / D1) * (N1 * t * t + t);
+        } else if (t < 2 / D1) {
+            t -= 1.5f / D1;
+            return N1 * t * t + 0.75f;
+        } else if (t < 2.5 / D1) {
+            t -= 2.25f / D1;
+            return N1 * t * t + 0.9375f;
+        } else if (t <= 1) {
+            t -= 2.625f / D1;
+            return N1 * t * t + 0.984375f;
+        }
+        return 1f;
+    }
+
+    @Override
+    public float getDiff(float x) {
+        if (x < 0) {
+            return 0f;
+        }
+        if (x < 1 / D1) {
+            return 2 * N1 * x / (1 + 1 / D1) + 1 / (1 + 1 / D1);
+        } else if (x < 2 / D1) {
+            return 2 * N1 * (x - 1.5f / D1);
+        } else if (x < 2.5 / D1) {
+            return 2 * N1 * (x - 2.25f / D1);
+        } else if (x <= 1) {
+            return 2 * N1 * (x - 2.625f / D1);
+        }
+        return 0f;
+    }
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
new file mode 100644
index 0000000..fd1ee03
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+class CubicEasing extends Easing {
+    float mType = 0;
+    float mX1 = 0f;
+    float mY1 = 0f;
+    float mX2 = 0f;
+    float mY2 = 0f;
+
+    private static final float[] STANDARD = {0.4f, 0.0f, 0.2f, 1f};
+    private static final float[] ACCELERATE = {0.4f, 0.05f, 0.8f, 0.7f};
+    private static final float[] DECELERATE = {0.0f, 0.0f, 0.2f, 0.95f};
+    private static final float[] LINEAR = {1f, 1f, 0f, 0f};
+    private static final float[] ANTICIPATE = {0.36f, 0f, 0.66f, -0.56f};
+    private static final float[] OVERSHOOT = {0.34f, 1.56f, 0.64f, 1f};
+
+    CubicEasing(int type) {
+        mType = type;
+        config(type);
+    }
+
+    CubicEasing(float x1, float y1, float x2, float y2) {
+        setup(x1, y1, x2, y2);
+    }
+
+    public void config(int type) {
+
+        switch (type) {
+            case CUBIC_STANDARD:
+                setup(STANDARD);
+                break;
+            case CUBIC_ACCELERATE:
+                setup(ACCELERATE);
+                break;
+            case CUBIC_DECELERATE:
+                setup(DECELERATE);
+                break;
+            case CUBIC_LINEAR:
+                setup(LINEAR);
+                break;
+            case CUBIC_ANTICIPATE:
+                setup(ANTICIPATE);
+                break;
+            case CUBIC_OVERSHOOT:
+                setup(OVERSHOOT);
+                break;
+        }
+        mType = type;
+    }
+
+    void setup(float[] values) {
+        setup(values[0], values[1], values[2], values[3]);
+    }
+
+    void setup(float x1, float y1, float x2, float y2) {
+        mX1 = x1;
+        mY1 = y1;
+        mX2 = x2;
+        mY2 = y2;
+    }
+
+    private float getX(float t) {
+        float t1 = 1 - t;
+        // no need for because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t)
+        float f1 = 3 * t1 * t1 * t;
+        float f2 = 3 * t1 * t * t;
+        float f3 = t * t * t;
+        return mX1 * f1 + mX2 * f2 + f3;
+    }
+
+    private float getY(float t) {
+        float t1 = 1 - t;
+        // no need for testing because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t)
+        float f1 = 3 * t1 * t1 * t;
+        float f2 = 3 * t1 * t * t;
+        float f3 = t * t * t;
+        return mY1 * f1 + mY2 * f2 + f3;
+    }
+
+    private float getDiffX(float t) {
+        float t1 = 1 - t;
+        return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
+    }
+
+    private float getDiffY(float t) {
+        float t1 = 1 - t;
+        return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
+    }
+
+    /**
+     * binary search for the region and linear interpolate the answer
+     */
+    public float getDiff(float x) {
+        float t = 0.5f;
+        float range = 0.5f;
+        while (range > D_ERROR) {
+            float tx = getX(t);
+            range *= 0.5;
+            if (tx < x) {
+                t += range;
+            } else {
+                t -= range;
+            }
+        }
+        float x1 = getX(t - range);
+        float x2 = getX(t + range);
+        float y1 = getY(t - range);
+        float y2 = getY(t + range);
+        return (y2 - y1) / (x2 - x1);
+    }
+
+    /**
+     * binary search for the region and linear interpolate the answer
+     */
+    public float get(float x) {
+        if (x <= 0.0f) {
+            return 0f;
+        }
+        if (x >= 1.0f) {
+            return 1.0f;
+        }
+        float t = 0.5f;
+        float range = 0.5f;
+        while (range > ERROR) {
+            float tx = getX(t);
+            range *= 0.5f;
+            if (tx < x) {
+                t += range;
+            } else {
+                t -= range;
+            }
+        }
+        float x1 = getX(t - range);
+        float x2 = getX(t + range);
+        float y1 = getY(t - range);
+        float y2 = getY(t + range);
+        return (y2 - y1) * (x - x1) / (x2 - x1) + y1;
+    }
+
+    private static final float ERROR = 0.01f;
+    private static final float D_ERROR = 0.0001f;
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
new file mode 100644
index 0000000..4ed9550
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * The standard interface to Easing functions
+ */
+public abstract class Easing {
+    int mType;
+    /**
+     * get the value at point x
+     */
+    public abstract float get(float x);
+
+    /**
+     * get the slope of the easing function at at x
+     */
+    public abstract float getDiff(float x);
+
+    public int getType() {
+        return mType;
+    }
+
+    public static final int CUBIC_STANDARD = 1;
+    public static final int CUBIC_ACCELERATE = 2;
+    public static final int CUBIC_DECELERATE = 3;
+    public static final int CUBIC_LINEAR = 4;
+    public static final int CUBIC_ANTICIPATE = 5;
+    public static final int CUBIC_OVERSHOOT = 6;
+    public static final int CUBIC_CUSTOM = 11;
+    public static final int SPLINE_CUSTOM = 12;
+    public static final int EASE_OUT_BOUNCE = 13;
+    public static final int EASE_OUT_ELASTIC = 14;
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
new file mode 100644
index 0000000..e269583
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Provide a bouncing Easing function
+ */
+public class ElasticOutCurve extends Easing {
+    private static final float F_PI = (float) Math.PI;
+    private static final float C4 = 2 * F_PI / 3;
+    private static final float TWENTY_PI = 20 * F_PI;
+    private static final float LOG_8 = (float) Math.log(8.0f);
+
+    @Override
+    public float get(float x) {
+        if (x <= 0) {
+            return 0.0f;
+        }
+        if (x >= 1) {
+            return 1.0f;
+        } else
+            return (float) (Math.pow(2.0f, -10 * x)
+                    * Math.sin((x * 10 - 0.75f) * C4) + 1);
+    }
+
+    @Override
+    public float getDiff(float x) {
+        if (x < 0 || x > 1) {
+            return 0.0f;
+        } else
+            return (float) ((5 * Math.pow(2.0f, (1 - 10 * x))
+                    * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2
+                    * F_PI * Math.sin(TWENTY_PI * x / 3))
+                    / 3));
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
new file mode 100644
index 0000000..4f484de
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Support Animation of the FloatExpression
+ */
+public class FloatAnimation extends Easing {
+    float[] mSpec;
+    // mSpec[0] = duration
+    // int(mSpec[1]) = num_of_param << 16 | type
+    // mSpec[2..1+num_of_param] params
+    // mSpec[2+num_of_param] starting Value
+    Easing mEasingCurve;
+    private int mType = CUBIC_STANDARD;
+    private float mDuration = 1;
+    private float mWrap = Float.NaN;
+    private float mInitialValue = Float.NaN;
+    private float mTargetValue = Float.NaN;
+    private float mScale = 1;
+    float mOffset = 0;
+
+    @Override
+    public String toString() {
+
+        String str = "type " + mType;
+        if (!Float.isNaN(mInitialValue)) {
+            str += " " + mInitialValue;
+        }
+        if (!Float.isNaN(mTargetValue)) {
+            str += " -> " + mTargetValue;
+        }
+        if (!Float.isNaN(mWrap)) {
+            str += "  % " + mWrap;
+        }
+
+        return str;
+    }
+
+    public FloatAnimation() {
+    }
+
+    public FloatAnimation(float... description) {
+        setAnimationDescription(description);
+    }
+
+    public FloatAnimation(int type,
+                          float duration,
+                          float[] description,
+                          float initialValue,
+                          float wrap) {
+        setAnimationDescription(packToFloatArray(duration,
+                type, description, initialValue, wrap));
+    }
+
+    /**
+     * packs spec into a float array
+     *
+     * @param duration
+     * @param type
+     * @param spec
+     * @param initialValue
+     * @return
+     */
+    public static float[] packToFloatArray(float duration,
+                                           int type,
+                                           float[] spec,
+                                           float initialValue,
+                                           float wrap) {
+        int count = 0;
+
+        if (!Float.isNaN(initialValue)) {
+            count++;
+        }
+        if (spec != null) {
+            count++;
+        }
+        if (spec != null || type != CUBIC_STANDARD) {
+            count++;
+            count += (spec == null) ? 0 : spec.length;
+        }
+        if (duration != 1 || count > 0) {
+            count++;
+        }
+        if (!Float.isNaN(initialValue)) {
+            count++;
+        }
+        if (!Float.isNaN(wrap)) {
+            count++;
+        }
+        float[] ret = new float[count];
+        int pos = 0;
+        int specLen = (spec == null) ? 0 : spec.length;
+
+        if (ret.length > 0) {
+            ret[pos++] = duration;
+
+        }
+        if (ret.length > 1) {
+            int wrapBit = (Float.isNaN(wrap)) ? 0 : 1;
+            int initBit = (Float.isNaN(initialValue)) ? 0 : 2;
+            int bits = type | ((wrapBit | initBit) << 8);
+            ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits);
+        }
+
+        if (specLen > 0) {
+            System.arraycopy(spec, 0, ret, pos, spec.length);
+            pos += spec.length;
+        }
+        if (!Float.isNaN(initialValue)) {
+            ret[pos++] = initialValue;
+        }
+        if (!Float.isNaN(wrap)) {
+            ret[pos] = wrap;
+        }
+        return ret;
+    }
+
+    /**
+     * Create an animation based on a float encoding of the animation
+     * @param description
+     */
+    public void setAnimationDescription(float[] description) {
+        mSpec = description;
+        mDuration = (mSpec.length == 0) ? 1 : mSpec[0];
+        int len = 0;
+        if (mSpec.length > 1) {
+            int num_type = Float.floatToRawIntBits(mSpec[1]);
+            mType = num_type & 0xFF;
+            boolean wrap = ((num_type >> 8) & 0x1) > 0;
+            boolean init = ((num_type >> 8) & 0x2) > 0;
+            len = (num_type >> 16) & 0xFFFF;
+            int off = 2 + len;
+            if (init) {
+                mInitialValue = mSpec[off++];
+            }
+            if (wrap) {
+                mWrap = mSpec[off];
+            }
+        }
+        create(mType, description, 2, len);
+    }
+
+    private void create(int type, float[] params, int offset, int len) {
+        switch (type) {
+            case CUBIC_STANDARD:
+            case CUBIC_ACCELERATE:
+            case CUBIC_DECELERATE:
+            case CUBIC_LINEAR:
+            case CUBIC_ANTICIPATE:
+            case CUBIC_OVERSHOOT:
+                mEasingCurve = new CubicEasing(type);
+                break;
+            case CUBIC_CUSTOM:
+                mEasingCurve = new CubicEasing(params[offset + 0],
+                        params[offset + 1],
+                        params[offset + 2],
+                        params[offset + 3]
+                );
+                break;
+            case EASE_OUT_BOUNCE:
+                mEasingCurve = new BounceCurve(type);
+                break;
+            case EASE_OUT_ELASTIC:
+                mEasingCurve = new ElasticOutCurve();
+                break;
+            case SPLINE_CUSTOM:
+                mEasingCurve = new StepCurve(params, offset, len);
+                break;
+        }
+    }
+
+    /**
+     * Get the duration the interpolate is to take
+     * @return duration in seconds
+     */
+    public float getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Set the initial Value
+     * @param value
+     */
+    public void setInitialValue(float value) {
+
+        if (Float.isNaN(mWrap)) {
+            mInitialValue = value;
+        } else {
+            mInitialValue = value % mWrap;
+        }
+        setScaleOffset();
+    }
+
+    /**
+     * Set the target value to interpolate to
+     * @param value
+     */
+    public void setTargetValue(float value) {
+        if (Float.isNaN(mWrap)) {
+            mTargetValue = value;
+        } else {
+            if (Math.abs((value % mWrap) + mWrap - mInitialValue)
+                    < Math.abs((value % mWrap) - mInitialValue)) {
+                mTargetValue = (value % mWrap) + mWrap;
+
+            } else {
+                mTargetValue = value % mWrap;
+            }
+        }
+        setScaleOffset();
+    }
+
+    public float getTargetValue() {
+        return mTargetValue;
+    }
+
+    private void setScaleOffset() {
+        if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) {
+            mScale = (mTargetValue - mInitialValue);
+            mOffset = mInitialValue;
+        } else {
+            mScale = 1;
+            mOffset = 0;
+        }
+    }
+
+    /**
+     * get the value at time t in seconds since start
+     */
+    public float get(float t) {
+        return mEasingCurve.get(t / mDuration)
+                * (mTargetValue - mInitialValue) + mInitialValue;
+    }
+
+    /**
+     * get the slope of the easing function at at x
+     */
+    public float getDiff(float t) {
+        return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
+    }
+
+    public float getInitialValue() {
+        return mInitialValue;
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
new file mode 100644
index 0000000..693deaf
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -0,0 +1,81 @@
+/*
+ * 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.internal.widget.remotecompose.core.operations.utilities.easing;
+
+/**
+ * Provides and interface to create easing functions
+ */
+public class GeneralEasing extends  Easing{
+    float[] mEasingData = new float[0];
+    Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD);
+
+    /**
+     * Set the curve based on the float encoding of it
+     * @param data
+     */
+    public void setCurveSpecification(float[] data) {
+        mEasingData = data;
+        createEngine();
+    }
+
+    public float[] getCurveSpecification() {
+        return mEasingData;
+    }
+
+    void createEngine() {
+        int type = Float.floatToRawIntBits(mEasingData[0]);
+        switch (type) {
+            case CUBIC_STANDARD:
+            case CUBIC_ACCELERATE:
+            case CUBIC_DECELERATE:
+            case CUBIC_LINEAR:
+            case CUBIC_ANTICIPATE:
+            case CUBIC_OVERSHOOT:
+                mEasingCurve = new CubicEasing(type);
+                break;
+            case CUBIC_CUSTOM:
+                mEasingCurve = new CubicEasing(mEasingData[1],
+                        mEasingData[2],
+                        mEasingData[3],
+                        mEasingData[5]
+                );
+                break;
+            case EASE_OUT_BOUNCE:
+                mEasingCurve = new BounceCurve(type);
+                break;
+        }
+    }
+
+    /**
+     * get the value at point x
+     */
+    public float get(float x) {
+        return mEasingCurve.get(x);
+    }
+
+    /**
+     * get the slope of the easing function at at x
+     */
+    public float getDiff(float x) {
+        return mEasingCurve.getDiff(x);
+    }
+
+    public int getType() {
+        return mEasingCurve.getType();
+    }
+
+
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
new file mode 100644
index 0000000..23930b9
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+import java.util.Arrays;
+
+/**
+ * This performs a spline interpolation in multiple dimensions
+ *
+ *
+ */
+public class MonotonicCurveFit  {
+    private static final String TAG = "MonotonicCurveFit";
+    private double[] mT;
+    private double[][] mY;
+    private double[][] mTangent;
+    private boolean mExtrapolate = true;
+    double[] mSlopeTemp;
+
+    /**
+     * create a collection of curves
+     * @param time the point along the curve
+     * @param y the parameter at those points
+     */
+    public MonotonicCurveFit(double[] time, double[][] y) {
+        final int n = time.length;
+        final int dim = y[0].length;
+        mSlopeTemp = new double[dim];
+        double[][] slope = new double[n - 1][dim]; // could optimize this out
+        double[][] tangent = new double[n][dim];
+        for (int j = 0; j < dim; j++) {
+            for (int i = 0; i < n - 1; i++) {
+                double dt = time[i + 1] - time[i];
+                slope[i][j] = (y[i + 1][j] - y[i][j]) / dt;
+                if (i == 0) {
+                    tangent[i][j] = slope[i][j];
+                } else {
+                    tangent[i][j] = (slope[i - 1][j] + slope[i][j]) * 0.5f;
+                }
+            }
+            tangent[n - 1][j] = slope[n - 2][j];
+        }
+
+        for (int i = 0; i < n - 1; i++) {
+            for (int j = 0; j < dim; j++) {
+                if (slope[i][j] == 0.) {
+                    tangent[i][j] = 0.;
+                    tangent[i + 1][j] = 0.;
+                } else {
+                    double a = tangent[i][j] / slope[i][j];
+                    double b = tangent[i + 1][j] / slope[i][j];
+                    double h = Math.hypot(a, b);
+                    if (h > 9.0) {
+                        double t = 3. / h;
+                        tangent[i][j] = t * a * slope[i][j];
+                        tangent[i + 1][j] = t * b * slope[i][j];
+                    }
+                }
+            }
+        }
+        mT = time;
+        mY = y;
+        mTangent = tangent;
+    }
+
+    /**
+     * Get the position of all curves at time t
+     * @param t
+     * @param v
+     */
+    public void getPos(double t, double[] v) {
+        final int n = mT.length;
+        final int dim = mY[0].length;
+        if (mExtrapolate) {
+            if (t <= mT[0]) {
+                getSlope(mT[0], mSlopeTemp);
+                for (int j = 0; j < dim; j++) {
+                    v[j] = mY[0][j] + (t - mT[0]) * mSlopeTemp[j];
+                }
+                return;
+            }
+            if (t >= mT[n - 1]) {
+                getSlope(mT[n - 1], mSlopeTemp);
+                for (int j = 0; j < dim; j++) {
+                    v[j] = mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j];
+                }
+                return;
+            }
+        } else {
+            if (t <= mT[0]) {
+                for (int j = 0; j < dim; j++) {
+                    v[j] = mY[0][j];
+                }
+                return;
+            }
+            if (t >= mT[n - 1]) {
+                for (int j = 0; j < dim; j++) {
+                    v[j] = mY[n - 1][j];
+                }
+                return;
+            }
+        }
+
+        for (int i = 0; i < n - 1; i++) {
+            if (t == mT[i]) {
+                for (int j = 0; j < dim; j++) {
+                    v[j] = mY[i][j];
+                }
+            }
+            if (t < mT[i + 1]) {
+                double h = mT[i + 1] - mT[i];
+                double x = (t - mT[i]) / h;
+                for (int j = 0; j < dim; j++) {
+                    double y1 = mY[i][j];
+                    double y2 = mY[i + 1][j];
+                    double t1 = mTangent[i][j];
+                    double t2 = mTangent[i + 1][j];
+                    v[j] = interpolate(h, x, y1, y2, t1, t2);
+                }
+                return;
+            }
+        }
+    }
+
+    /**
+     * Get the position of all curves at time t
+     * @param t
+     * @param v
+     */
+    public void getPos(double t, float[] v) {
+        final int n = mT.length;
+        final int dim = mY[0].length;
+        if (mExtrapolate) {
+            if (t <= mT[0]) {
+                getSlope(mT[0], mSlopeTemp);
+                for (int j = 0; j < dim; j++) {
+                    v[j] = (float) (mY[0][j] + (t - mT[0]) * mSlopeTemp[j]);
+                }
+                return;
+            }
+            if (t >= mT[n - 1]) {
+                getSlope(mT[n - 1], mSlopeTemp);
+                for (int j = 0; j < dim; j++) {
+                    v[j] = (float) (mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]);
+                }
+                return;
+            }
+        } else {
+            if (t <= mT[0]) {
+                for (int j = 0; j < dim; j++) {
+                    v[j] = (float) mY[0][j];
+                }
+                return;
+            }
+            if (t >= mT[n - 1]) {
+                for (int j = 0; j < dim; j++) {
+                    v[j] = (float) mY[n - 1][j];
+                }
+                return;
+            }
+        }
+
+        for (int i = 0; i < n - 1; i++) {
+            if (t == mT[i]) {
+                for (int j = 0; j < dim; j++) {
+                    v[j] = (float) mY[i][j];
+                }
+            }
+            if (t < mT[i + 1]) {
+                double h = mT[i + 1] - mT[i];
+                double x = (t - mT[i]) / h;
+                for (int j = 0; j < dim; j++) {
+                    double y1 = mY[i][j];
+                    double y2 = mY[i + 1][j];
+                    double t1 = mTangent[i][j];
+                    double t2 = mTangent[i + 1][j];
+                    v[j] = (float) interpolate(h, x, y1, y2, t1, t2);
+                }
+                return;
+            }
+        }
+    }
+
+    /**
+     * Get the position of the jth curve at time t
+     * @param t
+     * @param j
+     * @return
+     */
+    public double getPos(double t, int j) {
+        final int n = mT.length;
+        if (mExtrapolate) {
+            if (t <= mT[0]) {
+                return mY[0][j] + (t - mT[0]) * getSlope(mT[0], j);
+            }
+            if (t >= mT[n - 1]) {
+                return mY[n - 1][j] + (t - mT[n - 1]) * getSlope(mT[n - 1], j);
+            }
+        } else {
+            if (t <= mT[0]) {
+                return mY[0][j];
+            }
+            if (t >= mT[n - 1]) {
+                return mY[n - 1][j];
+            }
+        }
+
+        for (int i = 0; i < n - 1; i++) {
+            if (t == mT[i]) {
+                return mY[i][j];
+            }
+            if (t < mT[i + 1]) {
+                double h = mT[i + 1] - mT[i];
+                double x = (t - mT[i]) / h;
+                double y1 = mY[i][j];
+                double y2 = mY[i + 1][j];
+                double t1 = mTangent[i][j];
+                double t2 = mTangent[i + 1][j];
+                return interpolate(h, x, y1, y2, t1, t2);
+
+            }
+        }
+        return 0; // should never reach here
+    }
+
+    /**
+     * Get the slope of all the curves at position t
+     * @param t
+     * @param v
+     */
+    public void getSlope(double t, double[] v) {
+        final int n = mT.length;
+        int dim = mY[0].length;
+        if (t <= mT[0]) {
+            t = mT[0];
+        } else if (t >= mT[n - 1]) {
+            t = mT[n - 1];
+        }
+
+        for (int i = 0; i < n - 1; i++) {
+            if (t <= mT[i + 1]) {
+                double h = mT[i + 1] - mT[i];
+                double x = (t - mT[i]) / h;
+                for (int j = 0; j < dim; j++) {
+                    double y1 = mY[i][j];
+                    double y2 = mY[i + 1][j];
+                    double t1 = mTangent[i][j];
+                    double t2 = mTangent[i + 1][j];
+                    v[j] = diff(h, x, y1, y2, t1, t2) / h;
+                }
+                break;
+            }
+        }
+        return;
+    }
+
+    /**
+     * Get the slope of the j curve at position t
+     * @param t
+     * @param j
+     * @return
+     */
+    public double getSlope(double t, int j) {
+        final int n = mT.length;
+
+        if (t < mT[0]) {
+            t = mT[0];
+        } else if (t >= mT[n - 1]) {
+            t = mT[n - 1];
+        }
+        for (int i = 0; i < n - 1; i++) {
+            if (t <= mT[i + 1]) {
+                double h = mT[i + 1] - mT[i];
+                double x = (t - mT[i]) / h;
+                double y1 = mY[i][j];
+                double y2 = mY[i + 1][j];
+                double t1 = mTangent[i][j];
+                double t2 = mTangent[i + 1][j];
+                return diff(h, x, y1, y2, t1, t2) / h;
+            }
+        }
+        return 0; // should never reach here
+    }
+
+    public double[] getTimePoints() {
+        return mT;
+    }
+
+    /**
+     * Cubic Hermite spline
+     */
+    private static double interpolate(double h,
+                                      double x,
+                                      double y1,
+                                      double y2,
+                                      double t1,
+                                      double t2) {
+        double x2 = x * x;
+        double x3 = x2 * x;
+        return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1
+                + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2
+                + h * t1 * x;
+    }
+
+    /**
+     * Cubic Hermite spline slope differentiated
+     */
+    private static double diff(double h, double x, double y1, double y2, double t1, double t2) {
+        double x2 = x * x;
+        return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2
+                + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1;
+    }
+
+    /**
+     * This builds a monotonic spline to be used as a wave function
+     */
+    public static MonotonicCurveFit buildWave(String configString) {
+        // done this way for efficiency
+        String str = configString;
+        double[] values = new double[str.length() / 2];
+        int start = configString.indexOf('(') + 1;
+        int off1 = configString.indexOf(',', start);
+        int count = 0;
+        while (off1 != -1) {
+            String tmp = configString.substring(start, off1).trim();
+            values[count++] = Double.parseDouble(tmp);
+            off1 = configString.indexOf(',', start = off1 + 1);
+        }
+        off1 = configString.indexOf(')', start);
+        String tmp = configString.substring(start, off1).trim();
+        values[count++] = Double.parseDouble(tmp);
+
+        return buildWave(Arrays.copyOf(values, count));
+    }
+
+    private static MonotonicCurveFit buildWave(double[] values) {
+        int length = values.length * 3 - 2;
+        int len = values.length - 1;
+        double gap = 1.0 / len;
+        double[][] points = new double[length][1];
+        double[] time = new double[length];
+        for (int i = 0; i < values.length; i++) {
+            double v = values[i];
+            points[i + len][0] = v;
+            time[i + len] = i * gap;
+            if (i > 0) {
+                points[i + len * 2][0] = v + 1;
+                time[i + len * 2] = i * gap + 1;
+
+                points[i - 1][0] = v - 1 - gap;
+                time[i - 1] = i * gap + -1 - gap;
+            }
+        }
+
+        return new MonotonicCurveFit(time, points);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
new file mode 100644
index 0000000..6ed6548
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
+
+
+/**
+ * This class translates a series of floating point values into a continuous
+ * curve for use in an easing function including quantize functions
+ * it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)" it should start at 0 and end at one 1
+ */
+public class StepCurve extends Easing {
+    private static final boolean DEBUG = false;
+    MonotonicCurveFit mCurveFit;
+
+    public StepCurve(float[] params, int offset, int len) {
+        mCurveFit = genSpline(params, offset, len);
+    }
+
+    private static MonotonicCurveFit genSpline(float[] values, int off, int arrayLen) {
+        int length = arrayLen * 3 - 2;
+        int len = arrayLen - 1;
+        double gap = 1.0 / len;
+        double[][] points = new double[length][1];
+        double[] time = new double[length];
+        for (int i = 0; i < arrayLen; i++) {
+            double v = values[i + off];
+            points[i + len][0] = v;
+            time[i + len] = i * gap;
+            if (i > 0) {
+                points[i + len * 2][0] = v + 1;
+                time[i + len * 2] = i * gap + 1;
+
+                points[i - 1][0] = v - 1 - gap;
+                time[i - 1] = i * gap + -1 - gap;
+            }
+        }
+
+        MonotonicCurveFit ms = new MonotonicCurveFit(time, points);
+
+        return ms;
+    }
+
+    @Override
+    public float getDiff(float x) {
+        return (float) mCurveFit.getSlope(x, 0);
+    }
+
+
+    @Override
+    public float get(float x) {
+        return (float) mCurveFit.getPos(x, 0);
+    }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index bcda27a..d1c4d46 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -79,6 +79,15 @@
     }
 
     /**
+     * The delay in milliseconds to next repaint -1 = not needed 0 = asap
+     *
+     * @return delay in milliseconds to next repaint or -1
+     */
+    public int needsRepaint() {
+        return mDocument.needsRepaint();
+    }
+
+    /**
      * Returns true if the document can be displayed given this version of the player
      *
      * @param majorVersion the max major version supported by the player
@@ -89,5 +98,10 @@
         return mDocument.canBeDisplayed(majorVersion, minorVersion, capabilities);
     }
 
+    @Override
+    public String toString() {
+        return "Document{\n"
+                + mDocument + '}';
+    }
 }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index d0d6e69..ecb68bb 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -26,6 +26,7 @@
 import android.graphics.RadialGradient;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.RuntimeShader;
 import android.graphics.Shader;
 import android.graphics.SweepGradient;
 import android.graphics.Typeface;
@@ -33,6 +34,8 @@
 import com.android.internal.widget.remotecompose.core.PaintContext;
 import com.android.internal.widget.remotecompose.core.RemoteContext;
 import com.android.internal.widget.remotecompose.core.operations.ClipPath;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
+import com.android.internal.widget.remotecompose.core.operations.Utils;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
 import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges;
 
@@ -43,6 +46,7 @@
 public class AndroidPaintContext extends PaintContext {
     Paint mPaint = new Paint();
     Canvas mCanvas;
+    Rect mTmpRect = new Rect(); // use in calculation of bounds
 
     public AndroidPaintContext(RemoteContext context, Canvas canvas) {
         super(context);
@@ -177,6 +181,22 @@
     }
 
     @Override
+    public void getTextBounds(int textId, int start, int end, boolean monospace, float[] bounds) {
+        String str = getText(textId);
+        if (end == -1) {
+            end = str.length();
+        }
+
+        mPaint.getTextBounds(str, start, end, mTmpRect);
+
+        bounds[0] = mTmpRect.left;
+        bounds[1] = mTmpRect.top;
+        bounds[2] = monospace ? (mPaint.measureText(str, start, end) - mTmpRect.left)
+                : mTmpRect.right;
+        bounds[3] = mTmpRect.bottom;
+    }
+
+    @Override
     public void drawTextRun(int textID,
                             int start,
                             int end,
@@ -185,7 +205,16 @@
                             float x,
                             float y,
                             boolean rtl) {
-        String textToPaint = getText(textID).substring(start, end);
+
+        String textToPaint = getText(textID);
+        if (end == -1) {
+            if (start != 0) {
+                textToPaint = textToPaint.substring(start);
+            }
+        } else {
+            textToPaint = textToPaint.substring(start, end);
+        }
+
         mCanvas.drawText(textToPaint, x, y, mPaint);
     }
 
@@ -308,7 +337,7 @@
 
     @Override
     public void applyPaint(PaintBundle mPaintData) {
-        mPaintData.applyPaintChange(new PaintChanges() {
+        mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
             @Override
             public void setTextSize(float size) {
                 mPaint.setTextSize(size);
@@ -361,10 +390,8 @@
                     }
                 }
 
-
             }
 
-
             @Override
             public void setStrokeWidth(float width) {
                 mPaint.setStrokeWidth(width);
@@ -386,13 +413,37 @@
             }
 
             @Override
-            public void setShader(int shader, String shaderString) {
-
+            public void setShader(int shaderId) {
+                // TODO this stuff should check the shader creation
+                if (shaderId == 0) {
+                    mPaint.setShader(null);
+                    return;
+                }
+                ShaderData data = getShaderData(shaderId);
+                RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
+                String[] names = data.getUniformFloatNames();
+                for (int i = 0; i < names.length; i++) {
+                    String name = names[i];
+                    float[] val = data.getUniformFloats(name);
+                    shader.setFloatUniform(name, val);
+                }
+                names = data.getUniformIntegerNames();
+                for (int i = 0; i < names.length; i++) {
+                    String name = names[i];
+                    int[] val = data.getUniformInts(name);
+                    shader.setIntUniform(name, val);
+                }
+                names = data.getUniformBitmapNames();
+                for (int i = 0; i < names.length; i++) {
+                    String name = names[i];
+                    int val = data.getUniformBitmapId(name);
+                }
+                mPaint.setShader(shader);
             }
 
             @Override
             public void setImageFilterQuality(int quality) {
-                System.out.println(">>>>>>>>>>>> ");
+                Utils.log(" quality =" + quality);
             }
 
             @Override
@@ -420,7 +471,6 @@
                 mPaint.setFilterBitmap(filter);
             }
 
-
             @Override
             public void setAntiAlias(boolean aa) {
                 mPaint.setAntiAlias(aa);
@@ -437,7 +487,6 @@
 
                             case PaintBundle.COLOR_FILTER:
                                 mPaint.setColorFilter(null);
-                                System.out.println(">>>>>>>>>>>>> CLEAR!!!!");
                                 break;
                         }
                     }
@@ -446,12 +495,11 @@
                 }
             }
 
-            Shader.TileMode[] mTilesModes = new Shader.TileMode[]{
+            Shader.TileMode[] mTileModes = new Shader.TileMode[]{
                     Shader.TileMode.CLAMP,
                     Shader.TileMode.REPEAT,
                     Shader.TileMode.MIRROR};
 
-
             @Override
             public void setLinearGradient(int[] colors,
                                           float[] stops,
@@ -463,7 +511,7 @@
                 mPaint.setShader(new LinearGradient(startX,
                         startY,
                         endX,
-                        endY, colors, stops, mTilesModes[tileMode]));
+                        endY, colors, stops, mTileModes[tileMode]));
 
             }
 
@@ -475,7 +523,7 @@
                                           float radius,
                                           int tileMode) {
                 mPaint.setShader(new RadialGradient(centerX, centerY, radius,
-                        colors, stops, mTilesModes[tileMode]));
+                        colors, stops, mTileModes[tileMode]));
             }
 
             @Override
@@ -490,7 +538,6 @@
             @Override
             public void setColorFilter(int color, int mode) {
                 PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
-                System.out.println("setting color filter to " + pmode.name());
                 if (pmode != null) {
                     mPaint.setColorFilter(
                             new PorterDuffColorFilter(color, pmode));
@@ -500,10 +547,10 @@
     }
 
     @Override
-    public void mtrixScale(float scaleX,
-                           float scaleY,
-                           float centerX,
-                           float centerY) {
+    public void matrixScale(float scaleX,
+                            float scaleY,
+                            float centerX,
+                            float centerY) {
         if (Float.isNaN(centerX)) {
             mCanvas.scale(scaleX, scaleY);
         } else {
@@ -556,6 +603,11 @@
         }
     }
 
+    @Override
+    public void reset() {
+        mPaint.reset();
+    }
+
     private Path getPath(int path1Id,
                          int path2Id,
                          float tween,
@@ -599,5 +651,9 @@
     private String getText(int id) {
         return (String) mContext.mRemoteComposeState.getFromId(id);
     }
+
+    private ShaderData getShaderData(int id) {
+        return (ShaderData) mContext.mRemoteComposeState.getFromId(id);
+    }
 }
 
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index 270e96f..6e4893b 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -20,10 +20,15 @@
 import android.graphics.Canvas;
 
 import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
+import com.android.internal.widget.remotecompose.core.operations.ShaderData;
+
+import java.util.HashMap;
 
 /**
  * An implementation of Context for Android.
- *
+ * <p>
  * This is used to play the RemoteCompose operations on Android.
  */
 class AndroidRemoteContext extends RemoteContext {
@@ -33,6 +38,7 @@
             mPaintContext = new AndroidPaintContext(this, canvas);
         } else {
             // need to make sure to update the canvas for the current one
+            mPaintContext.reset();
             ((AndroidPaintContext) mPaintContext).setCanvas(canvas);
         }
         mWidth = canvas.getWidth();
@@ -50,13 +56,32 @@
         }
     }
 
+    static class VarName {
+        String mName;
+        int mId;
+        int mType;
+
+        VarName(String name, int id, int type) {
+            mName = name;
+            mId = id;
+            mType = type;
+        }
+    }
+
+    HashMap<String, VarName> mVarNameHashMap = new HashMap<>();
+
+    @Override
+    public void loadVariableName(String varName, int varId, int varType) {
+        mVarNameHashMap.put(varName, new VarName(varName, varId, varType));
+    }
+
     /**
      * Decode a byte array into an image and cache it using the given imageId
      *
-     * @oaram imageId the id of the image
-     * @param width with of image to be loaded
+     * @param width  with of image to be loaded
      * @param height height of image to be loaded
      * @param bitmap a byte array containing the image information
+     * @oaram imageId the id of the image
      */
     @Override
     public void loadBitmap(int imageId, int width, int height, byte[] bitmap) {
@@ -70,14 +95,66 @@
     public void loadText(int id, String text) {
         if (!mRemoteComposeState.containsId(id)) {
             mRemoteComposeState.cache(id, text);
+        } else {
+            mRemoteComposeState.update(id, text);
         }
     }
 
+    @Override
+    public String getText(int id) {
+        return (String) mRemoteComposeState.getFromId(id);
+    }
+
+    @Override
+    public void loadFloat(int id, float value) {
+        mRemoteComposeState.updateFloat(id, value);
+    }
+
+
+    @Override
+    public void loadColor(int id, int color) {
+        mRemoteComposeState.updateColor(id, color);
+    }
+
+    @Override
+    public void loadAnimatedFloat(int id, FloatExpression animatedFloat) {
+        mRemoteComposeState.cache(id, animatedFloat);
+    }
+
+    @Override
+    public void loadShader(int id, ShaderData value) {
+        mRemoteComposeState.cache(id, value);
+    }
+
+    @Override
+    public float getFloat(int id) {
+        return (float) mRemoteComposeState.getFloat(id);
+    }
+
+    @Override
+    public int getColor(int id) {
+        return mRemoteComposeState.getColor(id);
+    }
+
+    @Override
+    public void listensTo(int id, VariableSupport variableSupport) {
+        mRemoteComposeState.listenToVar(id, variableSupport);
+    }
+
+    @Override
+    public int updateOps() {
+        return mRemoteComposeState.getOpsToUpdate(this);
+    }
+
+    @Override
+    public ShaderData getShader(int id) {
+        return (ShaderData) mRemoteComposeState.getFromId(id);
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////////////////
     // Click handling
     ///////////////////////////////////////////////////////////////////////////////////////////////
 
-
     @Override
     public void addClickArea(int id,
                              int contentDescriptionId,
@@ -87,7 +164,7 @@
                              float bottom,
                              int metadataId) {
         String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId);
-        String  metadata = (String) mRemoteComposeState.getFromId(metadataId);
+        String metadata = (String) mRemoteComposeState.getFromId(metadataId);
         mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata);
     }
 }
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
index 672dae3..329178a 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
@@ -20,7 +20,6 @@
 import android.graphics.Paint;
 import android.view.View;
 
-
 /**
  * Implementation for the click handling
  */
@@ -40,7 +39,6 @@
         setContentDescription(contentDescription);
     }
 
-
     public void setDebug(boolean value) {
         if (mDebug != value) {
             mDebug = value;
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index a3bb73e..97d23c8 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -85,6 +85,7 @@
         mDocument.initializeContext(mARContext);
         setContentDescription(mDocument.getDocument().getContentDescription());
         requestLayout();
+        invalidate();
     }
 
     AndroidRemoteContext mARContext = new AndroidRemoteContext();
@@ -119,8 +120,7 @@
         removeAllViews();
     }
 
-
-    public  interface ClickCallbacks {
+    public interface ClickCallbacks {
         void click(int id, String metadata);
     }
 
@@ -213,6 +213,9 @@
         setMeasuredDimension(w, h);
     }
 
+    private int mCount;
+    private long mTime = System.nanoTime();
+
     @Override
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
@@ -224,6 +227,17 @@
         mARContext.mWidth = getWidth();
         mARContext.mHeight = getHeight();
         mDocument.paint(mARContext, mTheme);
+        if (mDebug) {
+            mCount++;
+            if (System.nanoTime() - mTime > 1000000000L) {
+                System.out.println(" count " + mCount + " fps");
+                mCount = 0;
+                mTime = System.nanoTime();
+            }
+        }
+        if (mDocument.needsRepaint() > 0) {
+            invalidate();
+        }
     }
 
 }
diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp
index 8fc13a8..9614864 100644
--- a/core/jni/android_database_SQLiteRawStatement.cpp
+++ b/core/jni/android_database_SQLiteRawStatement.cpp
@@ -83,6 +83,16 @@
     }
 }
 
+// If the last operation failed, throw an exception and return true.  Otherwise return false.
+static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
+    switch (sqlite3_errcode(db(stmtPtr))) {
+        case SQLITE_OK:
+        case SQLITE_DONE:
+        case SQLITE_ROW: return false;
+    }
+    throw_sqlite3_exception(env, db(stmtPtr), nullptr);
+    return true;
+}
 
 static jint bindParameterCount(JNIEnv* env, jclass, jlong stmtPtr) {
     return sqlite3_bind_parameter_count(stmt(stmtPtr));
@@ -223,17 +233,24 @@
 
 static jint columnBytes(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
     throwIfInvalidColumn(env, stmtPtr, col);
-    return sqlite3_column_bytes16(stmt(stmtPtr), col);
+    int r = sqlite3_column_bytes16(stmt(stmtPtr), col);
+    throwIfError(env, stmtPtr);
+    return r;
 }
 
-
 static jbyteArray columnBlob(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
     throwIfInvalidColumn(env, stmtPtr, col);
     const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
     if (blob == nullptr) {
+        if (throwIfError(env, stmtPtr)) {
+            return NULL;
+        }
         return (sqlite3_column_type(stmt(stmtPtr), col) == SQLITE_NULL) ? NULL : emptyArray;
     }
     size_t size = sqlite3_column_bytes(stmt(stmtPtr), col);
+    if (throwIfError(env, stmtPtr)) {
+        return NULL;
+    }
     jbyteArray result = env->NewByteArray(size);
     if (result == nullptr) {
         // An OutOfMemory exception will have been thrown.
@@ -248,9 +265,13 @@
     throwIfInvalidColumn(env, stmtPtr, col);
     const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
     if (blob == nullptr) {
+        throwIfError(env, stmtPtr);
         return 0;
     }
     jsize bsize = sqlite3_column_bytes(stmt(stmtPtr), col);
+    if (throwIfError(env, stmtPtr)) {
+        return 0;
+    }
     if (bsize == 0 || bsize <= srcOffset) {
         return 0;
     }
@@ -278,9 +299,13 @@
     throwIfInvalidColumn(env, stmtPtr, col);
     const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(stmt(stmtPtr), col));
     if (text == nullptr) {
+        throwIfError(env, stmtPtr);
         return NULL;
     }
     size_t length = sqlite3_column_bytes16(stmt(stmtPtr), col) / sizeof(jchar);
+    if (throwIfError(env, stmtPtr)) {
+        return NULL;
+    }
     return env->NewString(text, length);
 }
 
diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp
index b6bf617..89fdeeb 100644
--- a/core/jni/android_text_Hyphenator.cpp
+++ b/core/jni/android_text_Hyphenator.cpp
@@ -36,41 +36,43 @@
     return SYSTEM_HYPHENATOR_PREFIX + lowerLocale + SYSTEM_HYPHENATOR_SUFFIX;
 }
 
-static const uint8_t* mmapPatternFile(const std::string& locale) {
+static std::pair<const uint8_t*, size_t> mmapPatternFile(const std::string& locale) {
     const std::string hyFilePath = buildFileName(locale);
     const int fd = open(hyFilePath.c_str(), O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
-        return nullptr;  // Open failed.
+        return std::make_pair(nullptr, 0); // Open failed.
     }
 
     struct stat st = {};
     if (fstat(fd, &st) == -1) {  // Unlikely to happen.
         close(fd);
-        return nullptr;
+        return std::make_pair(nullptr, 0);
     }
 
     void* ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0 /* offset */);
     close(fd);
     if (ptr == MAP_FAILED) {
-        return nullptr;
+        return std::make_pair(nullptr, 0);
     }
-    return reinterpret_cast<const uint8_t*>(ptr);
+    return std::make_pair(reinterpret_cast<const uint8_t*>(ptr), st.st_size);
 }
 
 static void addHyphenatorWithoutPatternFile(const std::string& locale, int minPrefix,
         int minSuffix) {
-    minikin::addHyphenator(locale, minikin::Hyphenator::loadBinary(
-            nullptr, minPrefix, minSuffix, locale));
+    minikin::addHyphenator(locale,
+                           minikin::Hyphenator::loadBinary(nullptr, 0, minPrefix, minSuffix,
+                                                           locale));
 }
 
 static void addHyphenator(const std::string& locale, int minPrefix, int minSuffix) {
-    const uint8_t* ptr = mmapPatternFile(locale);
-    if (ptr == nullptr) {
+    std::pair<const uint8_t*, size_t> r = mmapPatternFile(locale);
+    if (r.first == nullptr) {
         ALOGE("Unable to find pattern file or unable to map it for %s", locale.c_str());
         return;
     }
-    minikin::addHyphenator(locale, minikin::Hyphenator::loadBinary(
-            ptr, minPrefix, minSuffix, locale));
+    minikin::addHyphenator(locale,
+                           minikin::Hyphenator::loadBinary(r.first, r.second, minPrefix, minSuffix,
+                                                           locale));
 }
 
 static void addHyphenatorAlias(const std::string& from, const std::string& to) {
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index f914bee..d32486c 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -203,55 +203,52 @@
     return true;
 }
 
-static void pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj,
-        float xOffset, float yOffset, PointerCoords* outRawPointerCoords) {
-    outRawPointerCoords->clear();
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_X,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x) - xOffset);
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_Y,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y) - yOffset);
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure));
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_SIZE,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size));
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor));
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor));
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor));
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
-            env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
-                                      env->GetFloatField(pointerCoordsObj,
-                                                         gPointerCoordsClassInfo.relativeX));
-    outRawPointerCoords->setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
-                                      env->GetFloatField(pointerCoordsObj,
-                                                         gPointerCoordsClassInfo.relativeY));
-    outRawPointerCoords->isResampled =
-            env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled);
+static PointerCoords pointerCoordsToNative(JNIEnv* env, jobject pointerCoordsObj) {
+    PointerCoords out{};
+    out.setAxisValue(AMOTION_EVENT_AXIS_X,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.x));
+    out.setAxisValue(AMOTION_EVENT_AXIS_Y,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.y));
+    out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.pressure));
+    out.setAxisValue(AMOTION_EVENT_AXIS_SIZE,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.size));
+    out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMajor));
+    out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.touchMinor));
+    out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMajor));
+    out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.toolMinor));
+    out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.orientation));
+    out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeX));
+    out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y,
+                     env->GetFloatField(pointerCoordsObj, gPointerCoordsClassInfo.relativeY));
+    out.isResampled = env->GetBooleanField(pointerCoordsObj, gPointerCoordsClassInfo.isResampled);
 
     BitSet64 bits =
             BitSet64(env->GetLongField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits));
     if (!bits.isEmpty()) {
-        jfloatArray valuesArray = jfloatArray(env->GetObjectField(pointerCoordsObj,
-                gPointerCoordsClassInfo.mPackedAxisValues));
+        jfloatArray valuesArray = jfloatArray(
+                env->GetObjectField(pointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisValues));
         if (valuesArray) {
-            jfloat* values = static_cast<jfloat*>(
-                    env->GetPrimitiveArrayCritical(valuesArray, NULL));
+            jfloat* values =
+                    static_cast<jfloat*>(env->GetPrimitiveArrayCritical(valuesArray, NULL));
 
             uint32_t index = 0;
             do {
                 uint32_t axis = bits.clearFirstMarkedBit();
-                outRawPointerCoords->setAxisValue(axis, values[index++]);
+                out.setAxisValue(axis, values[index++]);
             } while (!bits.isEmpty());
 
             env->ReleasePrimitiveArrayCritical(valuesArray, values, JNI_ABORT);
             env->DeleteLocalRef(valuesArray);
         }
     }
+    return out;
 }
 
 static jfloatArray obtainPackedAxisValuesArray(JNIEnv* env, uint32_t minSize,
@@ -303,14 +300,13 @@
     env->SetLongField(outPointerCoordsObj, gPointerCoordsClassInfo.mPackedAxisBits, outBits);
 }
 
-static void pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj,
-        PointerProperties* outPointerProperties) {
-    outPointerProperties->clear();
-    outPointerProperties->id = env->GetIntField(pointerPropertiesObj,
-            gPointerPropertiesClassInfo.id);
-    const int32_t toolType = env->GetIntField(pointerPropertiesObj,
-            gPointerPropertiesClassInfo.toolType);
-    outPointerProperties->toolType = static_cast<ToolType>(toolType);
+static PointerProperties pointerPropertiesToNative(JNIEnv* env, jobject pointerPropertiesObj) {
+    PointerProperties out{};
+    out.id = env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.id);
+    const int32_t toolType =
+            env->GetIntField(pointerPropertiesObj, gPointerPropertiesClassInfo.toolType);
+    out.toolType = static_cast<ToolType>(toolType);
+    return out;
 }
 
 static void pointerPropertiesFromNative(JNIEnv* env, const PointerProperties* pointerProperties,
@@ -343,15 +339,21 @@
         event = std::make_unique<MotionEvent>();
     }
 
-    PointerProperties pointerProperties[pointerCount];
-    PointerCoords rawPointerCoords[pointerCount];
+    ui::Transform transform;
+    transform.set(xOffset, yOffset);
+    const ui::Transform inverseTransform = transform.inverse();
+
+    std::vector<PointerProperties> pointerProperties;
+    pointerProperties.reserve(pointerCount);
+    std::vector<PointerCoords> rawPointerCoords;
+    rawPointerCoords.reserve(pointerCount);
 
     for (jint i = 0; i < pointerCount; i++) {
         jobject pointerPropertiesObj = env->GetObjectArrayElement(pointerPropertiesObjArray, i);
         if (!pointerPropertiesObj) {
             return 0;
         }
-        pointerPropertiesToNative(env, pointerPropertiesObj, &pointerProperties[i]);
+        pointerProperties.emplace_back(pointerPropertiesToNative(env, pointerPropertiesObj));
         env->DeleteLocalRef(pointerPropertiesObj);
 
         jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
@@ -359,19 +361,24 @@
             jniThrowNullPointerException(env, "pointerCoords");
             return 0;
         }
-        pointerCoordsToNative(env, pointerCoordsObj, xOffset, yOffset, &rawPointerCoords[i]);
+        rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj));
+        PointerCoords& coords = rawPointerCoords.back();
+        if (coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION) != 0.f) {
+            flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+                    AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
+        }
+        MotionEvent::calculateTransformedCoordsInPlace(coords, source, flags, inverseTransform);
         env->DeleteLocalRef(pointerCoordsObj);
     }
 
-    ui::Transform transform;
-    transform.set(xOffset, yOffset);
-    ui::Transform identityTransform;
+    static const ui::Transform kIdentityTransform;
     event->initialize(InputEvent::nextId(), deviceId, source, ui::LogicalDisplayId{displayId},
                       INVALID_HMAC, action, 0, flags, edgeFlags, metaState, buttonState,
                       static_cast<MotionClassification>(classification), transform, xPrecision,
                       yPrecision, AMOTION_EVENT_INVALID_CURSOR_POSITION,
-                      AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, downTimeNanos,
-                      eventTimeNanos, pointerCount, pointerProperties, rawPointerCoords);
+                      AMOTION_EVENT_INVALID_CURSOR_POSITION, kIdentityTransform, downTimeNanos,
+                      eventTimeNanos, pointerCount, pointerProperties.data(),
+                      rawPointerCoords.data());
 
     return reinterpret_cast<jlong>(event.release());
 }
@@ -391,7 +398,10 @@
         return;
     }
 
-    PointerCoords rawPointerCoords[pointerCount];
+    const ui::Transform inverseTransform = event->getTransform().inverse();
+
+    std::vector<PointerCoords> rawPointerCoords;
+    rawPointerCoords.reserve(pointerCount);
 
     for (size_t i = 0; i < pointerCount; i++) {
         jobject pointerCoordsObj = env->GetObjectArrayElement(pointerCoordsObjArray, i);
@@ -399,12 +409,13 @@
             jniThrowNullPointerException(env, "pointerCoords");
             return;
         }
-        pointerCoordsToNative(env, pointerCoordsObj, event->getRawXOffset(), event->getRawYOffset(),
-                              &rawPointerCoords[i]);
+        rawPointerCoords.emplace_back(pointerCoordsToNative(env, pointerCoordsObj));
+        MotionEvent::calculateTransformedCoordsInPlace(rawPointerCoords.back(), event->getSource(),
+                                                       event->getFlags(), inverseTransform);
         env->DeleteLocalRef(pointerCoordsObj);
     }
 
-    event->addSample(eventTimeNanos, rawPointerCoords);
+    event->addSample(eventTimeNanos, rawPointerCoords.data());
     event->setMetaState(event->getMetaState() | metaState);
 }
 
@@ -685,13 +696,15 @@
 
 static jint android_view_MotionEvent_nativeGetFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getFlags();
+    // Prevent private flags from being used in Java.
+    return event->getFlags() & ~AMOTION_EVENT_PRIVATE_FLAG_MASK;
 }
 
 static void android_view_MotionEvent_nativeSetFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr,
                                                     jint flags) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->setFlags(flags);
+    // Prevent private flags from being used from Java.
+    event->setFlags(flags & ~AMOTION_EVENT_PRIVATE_FLAG_MASK);
 }
 
 static jint android_view_MotionEvent_nativeGetEdgeFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index e831a7d..5365838 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -358,7 +358,8 @@
 
         jobject stats =
                 env->NewObject(gTransactionStatsClassInfo.clazz, gTransactionStatsClassInfo.ctor,
-                               latchTime, presentFence.get());
+                               latchTime,
+                               static_cast<jlong>(reinterpret_cast<uintptr_t>(presentFence.get())));
         env->CallVoidMethod(mTransactionCompletedListenerObject, gConsumerClassInfo.accept, stats);
         env->DeleteLocalRef(stats);
         DieIfException(env, "Uncaught exception in TransactionCompletedListener.");
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 8e4addd..0eb7c4a 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -49,6 +49,9 @@
         "/dev/dri/renderD129", // Fixes b/31172436
         "/dev/stune/foreground/tasks",
         "/dev/blkio/tasks",
+        "/metadata/aconfig/maps/system.package.map",
+        "/metadata/aconfig/maps/system.flag.map",
+        "/metadata/aconfig/boot/system.val"
 };
 
 static const char kFdPath[] = "/proc/self/fd";
diff --git a/core/proto/android/nfc/apdu_service_info.proto b/core/proto/android/nfc/apdu_service_info.proto
index fd110c4..9efdfcb 100644
--- a/core/proto/android/nfc/apdu_service_info.proto
+++ b/core/proto/android/nfc/apdu_service_info.proto
@@ -27,6 +27,20 @@
 message ApduServiceInfoProto {
     option (.android.msg_privacy).dest = DEST_EXPLICIT;
 
+    message AutoTransactMapping {
+        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional string aid = 1;
+        optional bool should_auto_transact = 2;
+    }
+
+    message AutoTransactPattern {
+        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional string regexp_pattern = 1;
+        optional bool should_auto_transact = 2;
+    }
+
     optional .android.content.ComponentNameProto component_name = 1;
     optional string description = 2;
     optional bool on_host = 3;
@@ -35,4 +49,7 @@
     repeated AidGroupProto static_aid_groups = 6;
     repeated AidGroupProto dynamic_aid_groups = 7;
     optional string settings_activity_name = 8;
+    optional bool should_default_to_observe_mode = 9;
+    repeated AutoTransactMapping auto_transact_mapping = 10;
+    repeated AutoTransactPattern auto_transact_patterns = 11;
 }
diff --git a/core/proto/android/nfc/card_emulation.proto b/core/proto/android/nfc/card_emulation.proto
index 9c3c6d7..81da30d 100644
--- a/core/proto/android/nfc/card_emulation.proto
+++ b/core/proto/android/nfc/card_emulation.proto
@@ -59,6 +59,7 @@
     optional .android.content.ComponentNameProto foreground_requested = 5;
     optional .android.content.ComponentNameProto settings_default = 6;
     optional bool prefer_foreground = 7;
+    optional .android.content.ComponentNameProto wallet_role_holder_payment_service = 8;
 }
 
 // Debugging information for com.android.nfc.cardemulation.EnabledNfcFServices
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 5fc2a59..6a0ec1d 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -122,6 +122,13 @@
     }
     optional Notification notification = 17;
 
+    message Pointer {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto pointer_fill_style = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto pointer_scale = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Pointer pointer = 37;
     optional SettingProto pointer_speed = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     message Ringtone {
@@ -268,5 +275,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 37;
+    // Next tag = 38;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6dbe44b..c71f9bd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3249,16 +3249,20 @@
     <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
         android:protectionLevel="signature|appop" />
 
-    <!-- Allows applications to access profiles with ACCESS_HIDDEN_PROFILES user property
-     <p>Protection level: normal
-     @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") -->
+    <!-- Allows applications to access profiles with
+        {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g.
+        {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}.
+        <p>Protection level: normal
+        @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") -->
     <permission android:name="android.permission.ACCESS_HIDDEN_PROFILES"
         android:label="@string/permlab_accessHiddenProfile"
         android:description="@string/permdesc_accessHiddenProfile"
         android:protectionLevel="normal" />
 
-    <!-- @SystemApi @hide Allows privileged applications to get details about hidden profile
-        users.
+    <!-- @SystemApi @hide Allows privileged applications to get details about profiles with
+        {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g.
+        {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}. Removes extra requirements such
+        as having {@link android.app.role.RoleManager#ROLE_HOME} role for LauncherApps APIs.
         @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") -->
     <permission
         android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL"
@@ -3322,13 +3326,18 @@
 
     <!-- Allows an application to manage device policy relating to time.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
-        APIs protected by this permission on users different to the calling user.-->
+        APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+    -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME"
                 android:protectionLevel="internal|role" />
 
     <!-- Allows an application to set the grant state of runtime permissions on packages.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS"
                 android:protectionLevel="internal|role" />
@@ -3336,6 +3345,8 @@
     <!-- Allows an application to manage the identity of the managing organization.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY"
                 android:protectionLevel="internal|role" />
@@ -3344,6 +3355,8 @@
         active policy.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE"
                 android:protectionLevel="internal|role" />
@@ -3351,6 +3364,8 @@
     <!-- Allows an application to manage backup service policy.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE"
                 android:protectionLevel="internal|role" />
@@ -3358,6 +3373,8 @@
     <!-- Allows an application to manage lock task policy.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"
                 android:protectionLevel="internal|role" />
@@ -3365,6 +3382,8 @@
     <!-- Allows an application to manage policy regarding modifying applications.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"
                 android:protectionLevel="internal|role" />
@@ -3372,6 +3391,8 @@
     <!-- Allows an application to manage installing from unknown sources policy.
         <p>MANAGE_SECURITY_CRITICAL_DEVICE_POLICY_ACROSS_USERS is required to call APIs protected
         by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES"
                 android:protectionLevel="internal|role" />
@@ -3379,6 +3400,8 @@
     <!-- Allows an application to manage application restrictions.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS"
                 android:protectionLevel="internal|role" />
@@ -3386,6 +3409,8 @@
     <!-- Allows an application to manage calling policy.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CALLS"
                 android:protectionLevel="internal|role" />
@@ -3393,6 +3418,8 @@
     <!-- Allows an application to manage debugging features policy.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"
                 android:protectionLevel="internal|role" />
@@ -3400,6 +3427,8 @@
     <!-- Allows an application to manage policy preventing users from modifying users.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS"
                 android:protectionLevel="internal|role" />
@@ -3407,6 +3436,8 @@
     <!-- Allows an application to manage safe boot policy.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT"
                 android:protectionLevel="internal|role" />
@@ -3415,6 +3446,8 @@
     enable and disable the microphone.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE"
                 android:protectionLevel="internal|role" />
@@ -3423,6 +3456,8 @@
     enable and disable the camera.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA"
                 android:protectionLevel="internal|role" />
@@ -3430,6 +3465,8 @@
     <!-- Allows an application to manage policy related to keyguard.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEYGUARD"
                 android:protectionLevel="internal|role" />
@@ -3437,6 +3474,8 @@
     <!-- Allows an application to set policy related to account management.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT"
                 android:protectionLevel="internal|role" />
@@ -3444,6 +3483,8 @@
     <!-- Allows an application to set policy related to hiding and suspending packages.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE"
                 android:protectionLevel="internal|role" />
@@ -3452,17 +3493,24 @@
     challenge on current user.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD"
                 android:protectionLevel="internal|role" />
 
-    <!-- Allows an application to set policy related to the status bar.-->
+    <!-- Allows an application to set policy related to the status bar.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+    -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_STATUS_BAR"
                 android:protectionLevel="internal|role" />
 
     <!-- Allows an application to set policy related to bluetooth.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH"
                 android:protectionLevel="internal|role" />
@@ -3470,6 +3518,8 @@
     <!-- Allows an application to set policy related to fun.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FUN"
                 android:protectionLevel="internal|role" />
@@ -3477,6 +3527,8 @@
     <!-- Allows an application to set policy related to airplane mode.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE"
                 android:protectionLevel="internal|role" />
@@ -3484,6 +3536,8 @@
     <!-- Allows an application to set policy related to mobile networks.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK"
                 android:protectionLevel="internal|role" />
@@ -3491,6 +3545,8 @@
     <!-- Allows an application to set policy related to physical media.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA"
                 android:protectionLevel="internal|role" />
@@ -3498,6 +3554,8 @@
     <!-- Allows an application to set policy related to sms.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SMS"
                 android:protectionLevel="internal|role" />
@@ -3505,6 +3563,8 @@
     <!-- Allows an application to set policy related to usb file transfers.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER"
                 android:protectionLevel="internal|role" />
@@ -3512,6 +3572,8 @@
     <!-- Allows an application to set policy related to lock credentials.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS"
                 android:protectionLevel="internal|role" />
@@ -3519,6 +3581,8 @@
     <!-- Allows an application to set policy related to Wifi.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIFI"
                 android:protectionLevel="internal|role" />
@@ -3526,6 +3590,8 @@
     <!-- Allows an application to set policy related to screen capture.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE"
                 android:protectionLevel="internal|role" />
@@ -3533,6 +3599,8 @@
     <!-- Allows an application to set policy related to input methods.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS"
                 android:protectionLevel="internal|role" />
@@ -3541,6 +3609,8 @@
      private DNS.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS"
                 android:protectionLevel="internal|role" />
@@ -3548,6 +3618,8 @@
     <!-- Allows an application to set policy related to the default sms application.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS"
                 android:protectionLevel="internal|role" />
@@ -3555,6 +3627,8 @@
     <!-- Allows an application to set policy related to profiles.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILES"
                 android:protectionLevel="internal|role" />
@@ -3563,6 +3637,8 @@
     cross-profile copy and paste).
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION"
                 android:protectionLevel="internal|role" />
@@ -3570,6 +3646,8 @@
     <!-- Allows an application to set policy related to VPNs.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_VPN"
                 android:protectionLevel="internal|role" />
@@ -3577,6 +3655,8 @@
     <!-- Allows an application to set policy related to audio output.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT"
                 android:protectionLevel="internal|role" />
@@ -3584,6 +3664,8 @@
     <!-- Allows an application to set policy related to the display.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY"
                 android:protectionLevel="internal|role" />
@@ -3591,6 +3673,8 @@
     <!-- Allows an application to set policy related to location.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCATION"
                 android:protectionLevel="internal|role" />
@@ -3598,6 +3682,8 @@
     <!-- Allows an application to set policy related to factory reset.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET"
                 android:protectionLevel="internal|role" />
@@ -3605,6 +3691,8 @@
     <!-- Allows an application to set policy related to the wallpaper.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WALLPAPER"
                 android:protectionLevel="internal|role" />
@@ -3612,6 +3700,8 @@
     <!-- Allows an application to set policy related to the usage of the contents of the screen.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CONTENT"
                 android:protectionLevel="internal|role" />
@@ -3619,6 +3709,8 @@
     <!-- Allows an application to set policy related to system dialogs.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS"
                 android:protectionLevel="internal|role" />
@@ -3626,6 +3718,8 @@
     <!-- Allows an application to set policy related to users running in the background.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND"
                 android:protectionLevel="internal|role" />
@@ -3633,6 +3727,8 @@
     <!-- Allows an application to set policy related to printing.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRINTING"
                 android:protectionLevel="internal|role" />
@@ -3641,12 +3737,16 @@
     nearby streaming).
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION"
                 android:protectionLevel="internal|role" />
 
     <!-- Allows an application to set policy related to <a
     href="https://www.threadgroup.org">Thread</a> network.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
         @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled")
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK"
@@ -3654,6 +3754,8 @@
 
     <!-- Allows an application to set policy related to sending assist content to a
         privileged app such as the Assistant app.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
         @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled")
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT"
@@ -3662,6 +3764,8 @@
     <!-- Allows an application to set policy related to windows.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WINDOWS"
                 android:protectionLevel="internal|role" />
@@ -3669,6 +3773,8 @@
     <!-- Allows an application to set policy related to locale.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCALE"
                 android:protectionLevel="internal|role" />
@@ -3676,6 +3782,8 @@
     <!-- Allows an application to set policy related to autofill.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUTOFILL"
                 android:protectionLevel="internal|role" />
@@ -3683,6 +3791,8 @@
     <!-- Allows an application to set policy related to users.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USERS"
                 android:protectionLevel="internal|role" />
@@ -3690,6 +3800,8 @@
     <!-- Allows an application to set policy related to certificates.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES"
                 android:protectionLevel="internal|role" />
@@ -3697,6 +3809,8 @@
     <!-- Allows an application to set policy related to override APNs.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_OVERRIDE_APN"
                 android:protectionLevel="internal|role" />
@@ -3704,6 +3818,8 @@
     <!-- Allows an application to set policy related to security logging.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING"
                 android:protectionLevel="internal|role" />
@@ -3719,6 +3835,8 @@
     <!-- Allows an application to set policy related to system updates.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES"
                 android:protectionLevel="internal|role" />
@@ -3726,6 +3844,8 @@
     <!-- Allows an application query system updates.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES"
                 android:protectionLevel="internal|role" />
@@ -3733,6 +3853,8 @@
     <!-- Allows an application to set policy related to private DNS.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRIVATE_DNS"
                 android:protectionLevel="internal|role" />
@@ -3740,6 +3862,8 @@
     <!-- Allows an application to set policy related to settings.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SETTINGS"
                 android:protectionLevel="internal|role" />
@@ -3747,17 +3871,24 @@
     <!-- Allows an application to set policy related to network logging.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NETWORK_LOGGING"
                 android:protectionLevel="internal|role" />
 
-    <!-- Allows an application to set policy related to usb data signalling.-->
+    <!-- Allows an application to set policy related to usb data signalling.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+    -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING"
                 android:protectionLevel="internal|role" />
 
     <!-- Allows an application to set policy related to suspending personal apps.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUSPEND_PERSONAL_APPS"
                 android:protectionLevel="internal|role" />
@@ -3765,13 +3896,17 @@
     <!-- Allows an application to set policy related to keeping uninstalled packages.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is
         required to call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEEP_UNINSTALLED_PACKAGES"
                 android:protectionLevel="internal|role" />
 
     <!-- Allows an application to manage policy related to accessibility.
-   <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
-   APIs protected by this permission on users different to the calling user.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to
+        call APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCESSIBILITY"
                 android:protectionLevel="internal|role" />
@@ -3779,6 +3914,8 @@
     <!-- Allows an application to manage policy related to common criteria mode.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE"
                 android:protectionLevel="internal|role" />
@@ -3786,6 +3923,8 @@
     <!-- Allows an application to manage policy related to metered data.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_METERED_DATA"
                 android:protectionLevel="internal|role" />
@@ -3793,6 +3932,8 @@
     <!-- Allows an application to set a network-independent global HTTP proxy.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROXY"
                 android:protectionLevel="internal|role" />
@@ -3800,6 +3941,8 @@
     <!-- Allows an application to request bugreports with user consent.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BUGREPORT"
                 android:protectionLevel="internal|role" />
@@ -3807,6 +3950,8 @@
     <!-- Allows an application to manage policy related to application user data.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA"
                 android:protectionLevel="internal|role" />
@@ -3815,6 +3960,8 @@
     permission.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK"
                 android:protectionLevel="internal|role" />
@@ -3830,6 +3977,8 @@
     <!-- Allows an application to manage policy related to system apps.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS"
                 android:protectionLevel="internal|role" />
@@ -3837,16 +3986,23 @@
     <!-- Allows an application to manage policy related to wiping data.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call
         APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIPE_DATA"
                 android:protectionLevel="internal|role" />
 
     <!-- Allows an application to manage policy related to the Memory Tagging Extension (MTE).
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MTE"
                 android:protectionLevel="internal|role" />
 
-    <!-- Allows an application to manage policy related to device identifiers. -->
+    <!-- Allows an application to manage policy related to device identifiers.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+    -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS"
                 android:protectionLevel="internal|role" />
 
@@ -3859,24 +4015,33 @@
 
     <!-- Allows an application to set policy related to subscriptions downloaded by an admin.
         <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
-            APIs protected by this permission on users different to the calling user.
-        @FlaggedApi("android.app.admin.flags.esim_management_enabled") -->
+        APIs protected by this permission on users different to the calling user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+        @FlaggedApi("android.app.admin.flags.esim_management_enabled")
+    -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"
         android:protectionLevel="internal|role" />
 
     <!-- Allows an application to manage policy related to block package uninstallation.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
         @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL"
         android:protectionLevel="internal|role" />
 
     <!-- Allows an application to manage policy related to camera toggle.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
         @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE"
         android:protectionLevel="internal|role" />
 
     <!-- Allows an application to manage policy related to microphone toggle.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
         @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE"
@@ -3885,16 +4050,21 @@
     <!-- Allows an application to set device policies outside the current user
         that are critical for securing data within the current user.
         <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
-            permissions across all users on the device provided they are required for securing data
-            within the current user.-->
+        permissions across all users on the device provided they are required for securing data
+        within the current user.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+    -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL"
                 android:protectionLevel="internal|role" />
 
     <!-- Allows an application to set device policies outside the current user
         that are required for securing device ownership without accessing user data.
         <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
-            permissions across all users on the device provided they do not grant access to user
-            data. -->
+        permissions across all users on the device provided they do not grant access to user data.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+    -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS"
                 android:protectionLevel="internal|role" />
 
@@ -3902,7 +4072,10 @@
         <p>Fuller form of {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS}
              that removes the restriction on accessing user data.
         <p>Holding this permission allows the use of any other held MANAGE_DEVICE_POLICY_*
-            permissions across all users on the device.-->
+            permissions across all users on the device.
+        <p>Protection level: internal|role
+        <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
+    -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL"
                 android:protectionLevel="internal|role" />
 
@@ -8194,6 +8367,17 @@
     <permission android:name="android.permission.SETUP_FSVERITY"
                 android:protectionLevel="signature|privileged"/>
 
+    <!--
+        @TestApi
+        Signature permission reserved for testing. This should never be used to
+        gate any actual functionality.
+        <p>
+        Protection level: signature
+        @hide
+    -->
+    <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
+                android:protectionLevel="signature"/>
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
@@ -8351,7 +8535,7 @@
         <activity android:name="android.accounts.GrantCredentialsPermissionActivity"
                 android:excludeFromRecents="true"
                 android:exported="true"
-                android:theme="@style/Theme.DeviceDefault.Light.DialogWhenLarge"
+                android:theme="@style/GrantCredentialsPermissionActivity"
                 android:process=":ui"
                 android:visibleToInstantApps="true">
         </activity>
diff --git a/core/res/res/drawable/floating_popup_background_light.xml b/core/res/res/drawable/floating_popup_background.xml
similarity index 84%
rename from core/res/res/drawable/floating_popup_background_light.xml
rename to core/res/res/drawable/floating_popup_background.xml
index 767140d..99acedf 100644
--- a/core/res/res/drawable/floating_popup_background_light.xml
+++ b/core/res/res/drawable/floating_popup_background.xml
@@ -16,8 +16,9 @@
 */
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
-    <solid android:color="@color/background_floating_material_light" />
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest"/>
     <corners android:radius="?android:attr/dialogCornerRadius" />
 </shape>
 
diff --git a/core/res/res/drawable/floating_popup_background_dark.xml b/core/res/res/drawable/floating_popup_background_dark.xml
deleted file mode 100644
index c4b4448..0000000
--- a/core/res/res/drawable/floating_popup_background_dark.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <solid android:color="@color/background_floating_material_dark" />
-    <corners android:radius="?android:attr/dialogCornerRadius" />
-</shape>
-
diff --git a/core/res/res/drawable/ic_swipe_down.xml b/core/res/res/drawable/ic_swipe_down.xml
new file mode 100644
index 0000000..15712d6
--- /dev/null
+++ b/core/res/res/drawable/ic_swipe_down.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="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M180,600L40,460L82,418L152,488Q146,461 143,434Q140,407 140,380Q140,298 167,221Q194,144 245,80L288,123Q245,179 222.5,244.5Q200,310 200,380Q200,406 203,431.5Q206,457 213,482L278,418L320,460L180,600ZM658,833Q635,841 611.5,840.5Q588,840 566,829L304,707L322,667Q332,647 350,634.5Q368,622 390,620L458,615L346,308Q340,292 347,277.5Q354,263 370,257Q386,251 400.5,258Q415,265 421,281L569,688L469,695L600,756Q607,759 615,759.5Q623,760 630,758L787,701Q818,690 832,659.5Q846,629 835,598L780,448Q774,432 781,417.5Q788,403 804,397Q820,391 834.5,398Q849,405 855,421L910,571Q933,634 905.5,693.5Q878,753 815,776L658,833ZM568,568L514,417Q508,401 515,386.5Q522,372 538,366Q554,360 568.5,367Q583,374 589,390L644,540L568,568ZM681,527L640,414Q634,398 641,383.5Q648,369 664,363Q680,357 694.5,364Q709,371 715,387L756,499L681,527ZM689,605L689,605L689,605Q689,605 689,605Q689,605 689,605L689,605Q689,605 689,605Q689,605 689,605L689,605L689,605Z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_bedtime.xml b/core/res/res/drawable/ic_zen_mode_type_bedtime.xml
new file mode 100644
index 0000000..7428a71
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_bedtime.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="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M524,920Q440,920 366.5,888Q293,856 238.5,801.5Q184,747 152,673.5Q120,600 120,516Q120,370 213,258.5Q306,147 450,120Q432,219 461,313.5Q490,408 561,479Q632,550 726.5,579Q821,608 920,590Q894,734 782,827Q670,920 524,920ZM524,840Q612,840 687,796Q762,752 805,675Q719,667 642,631.5Q565,596 504,535Q443,474 407,397Q371,320 364,234Q287,277 243.5,352.5Q200,428 200,516Q200,651 294.5,745.5Q389,840 524,840ZM504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Q504,535 504,535Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_driving.xml b/core/res/res/drawable/ic_zen_mode_type_driving.xml
new file mode 100644
index 0000000..3cc0066
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_driving.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="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M240,760L240,800Q240,817 228.5,828.5Q217,840 200,840L160,840Q143,840 131.5,828.5Q120,817 120,800L120,480L204,240Q210,222 225.5,211Q241,200 260,200L700,200Q719,200 734.5,211Q750,222 756,240L840,480L840,800Q840,817 828.5,828.5Q817,840 800,840L760,840Q743,840 731.5,828.5Q720,817 720,800L720,760L240,760ZM232,400L728,400L686,280L274,280L232,400ZM200,480L200,480L200,680L200,680L200,480ZM300,640Q325,640 342.5,622.5Q360,605 360,580Q360,555 342.5,537.5Q325,520 300,520Q275,520 257.5,537.5Q240,555 240,580Q240,605 257.5,622.5Q275,640 300,640ZM660,640Q685,640 702.5,622.5Q720,605 720,580Q720,555 702.5,537.5Q685,520 660,520Q635,520 617.5,537.5Q600,555 600,580Q600,605 617.5,622.5Q635,640 660,640ZM200,680L760,680L760,480L200,480L200,680Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_immersive.xml b/core/res/res/drawable/ic_zen_mode_type_immersive.xml
new file mode 100644
index 0000000..7091357
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_immersive.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="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M440,560L520,560L520,280L440,280L440,560ZM560,500L640,500L640,320L560,320L560,500ZM320,480L400,480L400,320L320,320L320,480ZM240,880L240,708Q183,656 151.5,586.5Q120,517 120,440Q120,290 225,185Q330,80 480,80Q605,80 701.5,153.5Q798,227 827,345L879,550Q884,569 872,584.5Q860,600 840,600L760,600L760,720Q760,753 736.5,776.5Q713,800 680,800L600,800L600,880L520,880L520,720L680,720Q680,720 680,720Q680,720 680,720L680,520L788,520L750,365Q727,274 652,217Q577,160 480,160Q364,160 282,241Q200,322 200,438Q200,498 224.5,552Q249,606 294,648L320,672L320,880L240,880ZM494,520L494,520L494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520Q494,520 494,520L494,520L494,520L494,520Q494,520 494,520Q494,520 494,520L494,520L494,520Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_managed.xml b/core/res/res/drawable/ic_zen_mode_type_managed.xml
new file mode 100644
index 0000000..5e224eb
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_managed.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="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M234,684Q285,645 348,622.5Q411,600 480,600Q549,600 612,622.5Q675,645 726,684Q761,643 780.5,591Q800,539 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,539 179.5,591Q199,643 234,684ZM480,520Q421,520 380.5,479.5Q340,439 340,380Q340,321 380.5,280.5Q421,240 480,240Q539,240 579.5,280.5Q620,321 620,380Q620,439 579.5,479.5Q539,520 480,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q533,800 580,784.5Q627,769 666,740Q627,711 580,695.5Q533,680 480,680Q427,680 380,695.5Q333,711 294,740Q333,769 380,784.5Q427,800 480,800ZM480,440Q506,440 523,423Q540,406 540,380Q540,354 523,337Q506,320 480,320Q454,320 437,337Q420,354 420,380Q420,406 437,423Q454,440 480,440ZM480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380Q480,380 480,380ZM480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Q480,740 480,740Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_other.xml b/core/res/res/drawable/ic_zen_mode_type_other.xml
new file mode 100644
index 0000000..d236b0d
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_other.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="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <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/core/res/res/drawable/ic_zen_mode_type_schedule_calendar.xml b/core/res/res/drawable/ic_zen_mode_type_schedule_calendar.xml
new file mode 100644
index 0000000..4000489
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_schedule_calendar.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="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+
+    <path android:fillColor="@android:color/white"
+          android:pathData="M17.0,12.0l-5.0,0.0l0.0,5.0l5.0,0.0l0.0,-5.0zM16.0,1.0l0.0,2.0L8.0,3.0L8.0,1.0L6.0,1.0l0.0,2.0L5.0,3.0c-1.11,0.0 -1.9,0.9 -1.99,2.0L3.0,19.0c0.0,1.0 0.89,2.0 2.0,2.0l14.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L21.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-1.0,0.0L18.0,1.0l-2.0,0.0zm3.0,18.0L5.0,19.0L5.0,8.0l14.0,0.0l0.0,11.0z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_zen_mode_type_schedule_time.xml b/core/res/res/drawable/ic_zen_mode_type_schedule_time.xml
new file mode 100644
index 0000000..57d596a
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_schedule_time.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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M612,668L668,612L520,464L520,280L440,280L440,496L612,668ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM480,800Q613,800 706.5,706.5Q800,613 800,480Q800,347 706.5,253.5Q613,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,613 253.5,706.5Q347,800 480,800Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_theater.xml b/core/res/res/drawable/ic_zen_mode_type_theater.xml
new file mode 100644
index 0000000..cc66b32
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_theater.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="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M160,840L160,120L240,120L240,200L320,200L320,120L640,120L640,200L720,200L720,120L800,120L800,840L720,840L720,760L640,760L640,840L320,840L320,760L240,760L240,840L160,840ZM240,680L320,680L320,600L240,600L240,680ZM240,520L320,520L320,440L240,440L240,520ZM240,360L320,360L320,280L240,280L240,360ZM640,680L720,680L720,600L640,600L640,680ZM640,520L720,520L720,440L640,440L640,520ZM640,360L720,360L720,280L640,280L640,360ZM400,760L560,760L560,200L400,200L400,760ZM400,200L400,200L560,200L560,200L400,200Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_unknown.xml b/core/res/res/drawable/ic_zen_mode_type_unknown.xml
new file mode 100644
index 0000000..c1afd44
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_unknown.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="?android:attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,880Q407,871 335,840.5Q263,810 206.5,753Q150,696 115,609Q80,522 80,400L80,360L120,360Q171,360 225,373Q279,386 326,412Q338,326 380.5,235.5Q423,145 480,80Q537,145 579.5,235.5Q622,326 634,412Q681,386 735,373Q789,360 840,360L880,360L880,400Q880,522 845,609Q810,696 753.5,753Q697,810 625.5,840.5Q554,871 480,880ZM478,798Q467,632 379.5,547Q292,462 162,442Q173,613 263.5,697Q354,781 478,798ZM480,544Q495,522 516.5,498.5Q538,475 558,458Q556,401 535.5,339Q515,277 480,218Q445,277 424.5,339Q404,401 402,458Q422,475 444,498.5Q466,522 480,544ZM558,780Q595,768 635,745Q675,722 709.5,682.5Q744,643 768.5,584Q793,525 798,442Q704,456 633,504.5Q562,553 524,628Q536,660 544.5,698Q553,736 558,780ZM480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544ZM558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780ZM478,798Q478,798 478,798Q478,798 478,798Q478,798 478,798Q478,798 478,798ZM524,628L524,628Q524,628 524,628Q524,628 524,628L524,628L524,628L524,628Q524,628 524,628Q524,628 524,628L524,628Q524,628 524,628Q524,628 524,628ZM480,880L480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880L480,880Q480,880 480,880Q480,880 480,880L480,880Q480,880 480,880Q480,880 480,880L480,880Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/immersive_cling_light_bg_circ.xml b/core/res/res/drawable/immersive_cling_bg.xml
similarity index 67%
rename from core/res/res/drawable/immersive_cling_light_bg_circ.xml
rename to core/res/res/drawable/immersive_cling_bg.xml
index df5d5ad..de29c32 100644
--- a/core/res/res/drawable/immersive_cling_light_bg_circ.xml
+++ b/core/res/res/drawable/immersive_cling_bg.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2015 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.
@@ -15,12 +15,10 @@
   ~ limitations under the License
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-
-    <solid android:color="#80ffffff" />
-
-    <size
-        android:height="76dp"
-        android:width="76dp" />
-
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners
+        android:bottomLeftRadius="28dp"
+        android:bottomRightRadius="28dp"/>
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
 </shape>
diff --git a/core/res/res/drawable/immersive_cling_bg_circ.xml b/core/res/res/drawable/immersive_cling_bg_circ.xml
deleted file mode 100644
index 4731bbd..0000000
--- a/core/res/res/drawable/immersive_cling_bg_circ.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-
-    <solid android:color="@color/white" />
-
-    <size
-        android:height="56dp"
-        android:width="56dp" />
-
-</shape>
diff --git a/core/res/res/drawable/immersive_cling_btn_bg.xml b/core/res/res/drawable/immersive_cling_btn_bg.xml
new file mode 100644
index 0000000..df49e38
--- /dev/null
+++ b/core/res/res/drawable/immersive_cling_btn_bg.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.
+  -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <ripple android:color="?android:attr/colorControlHighlight">
+        <item android:id="@android:id/mask">
+            <shape android:shape="rectangle">
+                <solid android:color="@android:color/white" />
+                <corners android:radius="28dp" />
+            </shape>
+        </item>
+        <item>
+            <shape android:shape="rectangle">
+                <corners android:radius="28dp" />
+                <solid android:color="?android:attr/colorAccent" />
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_alias_vector.xml b/core/res/res/drawable/pointer_alias_vector.xml
index 74dd6a0..035a099 100644
--- a/core/res/res/drawable/pointer_alias_vector.xml
+++ b/core/res/res/drawable/pointer_alias_vector.xml
@@ -28,6 +28,6 @@
         android:fillColor="#FFFFFF"
         android:pathData="M15.313 12.177a3 3 0 0 0-.416-.633l-.459-.534-.353.609a4.2 4.2 0 0 1-1.801 1.675 4.2 4.2 0 0 1-1.977.429l-.704-.02.213.671q.066.208.164.409l.975 1.995a2.967 2.967 0 1 0 5.332-2.606zm-.827 5.066a1.97 1.97 0 0 1-2.632-.904l-.81-1.658a5.2 5.2 0 0 0 1.68-.489 5.2 5.2 0 0 0 1.771-1.414l.896 1.833a1.97 1.97 0 0 1-.905 2.632m-3.697-7.565a4.2 4.2 0 0 1 1.977-.429l.704.02-.213-.671a3 3 0 0 0-.164-.409l-.975-1.995A2.967 2.967 0 1 0 6.785 8.8l.975 1.995q.172.35.416.633l.459.534.353-.609a4.2 4.2 0 0 1 1.801-1.675m-2.21.516-.895-1.833a1.968 1.968 0 1 1 3.536-1.728l.81 1.658a5.2 5.2 0 0 0-1.68.489 5.2 5.2 0 0 0-1.771 1.414m3.151 1.965a3 3 0 0 0 1.02-.818l.755-.95-1.205.142a2.97 2.97 0 0 0-1.975 1.1l-.755.95 1.205-.142c.324-.039.646-.132.955-.282" />
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M16.449 11.622a4.2 4.2 0 0 0-1.555-1.728l-.234-.146-.001-.276a4.2 4.2 0 0 0-.431-1.838l-.975-1.995a4.232 4.232 0 1 0-7.604 3.716l.975 1.995a4.2 4.2 0 0 0 1.555 1.729l.234.146.001.276c.002.617.141 1.244.431 1.838l.975 1.995a4.232 4.232 0 1 0 7.604-3.716zm-7.814.34-.459-.534a3 3 0 0 1-.416-.633L6.785 8.8a2.967 2.967 0 1 1 5.332-2.606l.975 1.995q.098.202.164.409l.214.672-.704-.02a4.2 4.2 0 0 0-1.977.429 4.2 4.2 0 0 0-1.801 1.675zm1.689-.33a2.97 2.97 0 0 1 1.975-1.1l1.205-.142-.755.95a2.95 2.95 0 0 1-1.02.818 3 3 0 0 1-.955.281l-1.204.143zm4.601 6.51a2.967 2.967 0 0 1-3.969-1.363l-.975-1.995a3 3 0 0 1-.164-.409l-.213-.671.704.02a4.2 4.2 0 0 0 1.977-.429 4.2 4.2 0 0 0 1.801-1.675l.353-.609.459.534q.245.284.416.633l.975 1.995a2.97 2.97 0 0 1-1.364 3.969" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_all_scroll_vector.xml b/core/res/res/drawable/pointer_all_scroll_vector.xml
index 1692e5e..45ad98c 100644
--- a/core/res/res/drawable/pointer_all_scroll_vector.xml
+++ b/core/res/res/drawable/pointer_all_scroll_vector.xml
@@ -21,7 +21,7 @@
         android:viewportHeight="24">
     <path
         android:pathData="M12.93 4.54a1.06 1.06 0 0 0-1.85 0L9.32 7.6c-.4.71.1 1.6.92 1.6h.82v1.86H9.2v-.84c0-.82-.88-1.33-1.6-.93l-3.06 1.76c-.7.41-.7 1.44 0 1.85l3.07 1.76c.7.4 1.6-.1 1.6-.93v-.79h1.86v1.87h-.82c-.81 0-1.33.88-.92 1.6l1.76 3.06c.4.71 1.44.71 1.85 0l1.75-3.07c.41-.7-.1-1.6-.92-1.6h-.82v-1.86h1.86v.8c0 .81.89 1.32 1.6.92l3.07-1.76c.7-.41.7-1.44 0-1.85L16.4 9.3c-.71-.4-1.6.1-1.6.93v.84h-1.86V9.2h.82c.82 0 1.33-.89.92-1.6l-1.75-3.06z"
-        android:fillColor="#000000"/>
+        android:fillColor="?attr/pointerIconVectorFill"/>
     <path
         android:pathData="M12 4c.36 0 .72.18.93.54l1.75 3.06c.41.71-.1 1.6-.92 1.6h-.82v1.86h1.86v-.84a1.07 1.07 0 0 1 1.6-.92l3.06 1.75c.72.41.72 1.44 0 1.85l-3.06 1.76a1.07 1.07 0 0 1-1.6-.92v-.8h-1.86v1.87h.82c.82 0 1.33.88.92 1.6l-1.75 3.06a1.07 1.07 0 0 1-1.85 0L9.32 16.4c-.4-.7.1-1.6.93-1.6h.81v-1.86H9.2v.8a1.07 1.07 0 0 1-1.6.92L4.54 12.9a1.06 1.06 0 0 1 0-1.85L7.6 9.3a1.07 1.07 0 0 1 1.6.92v.85h1.86V9.2h-.82c-.81 0-1.33-.89-.92-1.6l1.76-3.06c.2-.36.56-.54.92-.54m0-1c-.74 0-1.41.39-1.79 1.04L8.45 7.1c-.18.33-.28.7-.27 1.05h-.05c-.36 0-.71.1-1.03.28l-3.06 1.76a2.05 2.05 0 0 0 0 3.58l3.06 1.75c.32.18.67.28 1.03.28h.05c-.01.38.08.76.28 1.1l1.75 3.07c.38.65 1.05 1.03 1.8 1.03s1.41-.38 1.78-1.03l1.76-3.07c.2-.34.3-.72.28-1.1h.04c.36 0 .71-.1 1.03-.28l3.06-1.75a2.07 2.07 0 0 0 0-3.58L16.9 8.43a2.07 2.07 0 0 0-1.03-.28h-.04c0-.36-.09-.72-.28-1.05L13.8 4.04A2.04 2.04 0 0 0 12 3z"
         android:fillColor="#FFFFFF"/>
diff --git a/core/res/res/drawable/pointer_arrow_vector.xml b/core/res/res/drawable/pointer_arrow_vector.xml
index 562f0c0..2614170 100644
--- a/core/res/res/drawable/pointer_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_arrow_vector.xml
@@ -21,7 +21,7 @@
         android:viewportHeight="24">
     <path
         android:pathData="M16.34 11.18 6.77 4.02a1.78 1.78 0 0 0-1.88-.17c-.63.31-1 .91-1 1.6l.01 11.96c0 .9.6 1.46 1.15 1.67a1.74 1.74 0 0 0 1.98-.45l2.96-3.19c.3-.32.7-.52 1.13-.56l4.33-.47a1.8 1.8 0 0 0 .89-3.23z"
-        android:fillColor="#000000"/>
+        android:fillColor="?attr/pointerIconVectorFill"/>
     <path
         android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27 2.75 2.75 0 0 0-1.55 2.51l.01 11.95a2.78 2.78 0 0 0 2.82 2.8c.77 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92z"
         android:fillColor="#FFFFFF"/>
diff --git a/core/res/res/drawable/pointer_cell_vector.xml b/core/res/res/drawable/pointer_cell_vector.xml
index 044a4f4..cead1c4 100644
--- a/core/res/res/drawable/pointer_cell_vector.xml
+++ b/core/res/res/drawable/pointer_cell_vector.xml
@@ -22,6 +22,6 @@
         android:fillColor="#FFFFFF"
         android:pathData="M19 9.667h-4.668V5a2 2 0 0 0-2-2h-.667a2 2 0 0 0-2 2v4.667H5a2 2 0 0 0-2 2v.667a2 2 0 0 0 2 2h4.665V19a2 2 0 0 0 2 2h.667a2 2 0 0 0 2-2v-4.666H19a2 2 0 0 0 2-2v-.667a2 2 0 0 0-2-2m1 2.667a1 1 0 0 1-1 1h-5.668V19a1 1 0 0 1-1 1h-.667a1 1 0 0 1-1-1v-5.666H5a1 1 0 0 1-1-1v-.667a1 1 0 0 1 1-1h5.665V5a1 1 0 0 1 1-1h.667a1 1 0 0 1 1 1v5.667H19a1 1 0 0 1 1 1z" />
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M19 10.667h-5.668V5a1 1 0 0 0-1-1h-.667a1 1 0 0 0-1 1v5.667H5a1 1 0 0 0-1 1v.667a1 1 0 0 0 1 1h5.665V19a1 1 0 0 0 1 1h.667a1 1 0 0 0 1-1v-5.666H19a1 1 0 0 0 1-1v-.667a1 1 0 0 0-1-1" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_context_menu_vector.xml b/core/res/res/drawable/pointer_context_menu_vector.xml
index 8e954d2..fb2af43 100644
--- a/core/res/res/drawable/pointer_context_menu_vector.xml
+++ b/core/res/res/drawable/pointer_context_menu_vector.xml
@@ -26,9 +26,9 @@
         android:fillColor="#FFFFFF"
         android:pathData="M16.938 10.38 7.372 3.216a2.77 2.77 0 0 0-2.931-.262A2.75 2.75 0 0 0 2.894 5.46l.009 11.951a2.785 2.785 0 0 0 1.776 2.604c.33.129.691.197 1.044.197a2.75 2.75 0 0 0 2.031-.897l2.969-3.193a.8.8 0 0 1 .5-.25l4.336-.467c1.397-.15 2.157-1.153 2.401-2.041a2.785 2.785 0 0 0-1.022-2.984m.058 2.718c-.157.571-.645 1.216-1.544 1.312l-4.335.467a1.8 1.8 0 0 0-1.126.563l-2.97 3.193a1.74 1.74 0 0 1-1.298.578 1.9 1.9 0 0 1-.678-.128c-.551-.217-1.141-.771-1.142-1.674l-.009-11.95c0-.697.371-1.299.994-1.611.262-.131.538-.196.813-.196.377 0 .75.123 1.072.365l9.566 7.163c.723.542.814 1.346.657 1.918" />
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" />
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M19.475 3.461h-2.66c-.37 0-.67.3-.67.67v2.66c0 .37.3.67.67.67h2.66c.37 0 .67-.3.67-.67v-2.66a.67.67 0 0 0-.67-.67m-.3 3.062h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6m0-.868h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6m0-.885h-2.067a.3.3 0 1 1 0-.6h2.067a.3.3 0 1 1 0 .6" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_copy_vector.xml b/core/res/res/drawable/pointer_copy_vector.xml
index b1e8995..3f13868 100644
--- a/core/res/res/drawable/pointer_copy_vector.xml
+++ b/core/res/res/drawable/pointer_copy_vector.xml
@@ -19,8 +19,8 @@
     android:viewportHeight="24"
     android:viewportWidth="24">
     <group>
-        <path android:fillColor="#FFFFFF" android:pathData="M17.5 2c-2.104 0-3.861 1.457-4.351 3.41A4.5 4.5 0 0 0 13 6.5c0 .344.047.675.12.997-.062-.002-.122-.009-.185-.009q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.739a4.4 4.4 0 0 0 1-.37C20.969 9.778 22 8.265 22 6.5 22 4.019 19.981 2 17.5 2m1.985 7.364a3.6 3.6 0 0 1-1 .478A3.5 3.5 0 0 1 17.5 10a3.5 3.5 0 0 1-3.486-3.358C14.012 6.594 14 6.549 14 6.5c0-.328.06-.639.145-.941C14.559 4.088 15.898 3 17.5 3 19.43 3 21 4.57 21 6.5a3.47 3.47 0 0 1-1.515 2.864" />
-        <path android:fillColor="#FFFFFF" android:pathData="M19.299 6H18V4.7a.5.5 0 0 0-1 0V6h-1.301a.5.5 0 0 0 0 1H17v1.3a.5.5 0 0 0 1 0V7h1.299a.5.5 0 0 0 0-1" />
+        <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M17.5 2c-2.104 0-3.861 1.457-4.351 3.41A4.5 4.5 0 0 0 13 6.5c0 .344.047.675.12.997-.062-.002-.122-.009-.185-.009q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.739a4.4 4.4 0 0 0 1-.37C20.969 9.778 22 8.265 22 6.5 22 4.019 19.981 2 17.5 2m1.985 7.364a3.6 3.6 0 0 1-1 .478A3.5 3.5 0 0 1 17.5 10a3.5 3.5 0 0 1-3.486-3.358C14.012 6.594 14 6.549 14 6.5c0-.328.06-.639.145-.941C14.559 4.088 15.898 3 17.5 3 19.43 3 21 4.57 21 6.5a3.47 3.47 0 0 1-1.515 2.864" />
+        <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M19.299 6H18V4.7a.5.5 0 0 0-1 0V6h-1.301a.5.5 0 0 0 0 1H17v1.3a.5.5 0 0 0 1 0V7h1.299a.5.5 0 0 0 0-1" />
     </group>
     <path
         android:fillColor="#000000"
diff --git a/core/res/res/drawable/pointer_crosshair_vector.xml b/core/res/res/drawable/pointer_crosshair_vector.xml
index b2e7e8a..8a50d1b 100644
--- a/core/res/res/drawable/pointer_crosshair_vector.xml
+++ b/core/res/res/drawable/pointer_crosshair_vector.xml
@@ -23,6 +23,6 @@
         android:pathData="M19.25 10.25h-5.5v-5.5a1.75 1.75 0 0 0-3.5 0v5.5h-5.5a1.75 1.75 0 0 0 0 3.5h5.5v5.5a1.75 1.75 0 0 0 3.5 0v-5.5h5.5a1.75 1.75 0 0 0 0-3.5m0 2.5h-6.5v6.5a.75.75 0 0 1-1.5 0v-6.5h-6.5a.75.75 0 0 1 0-1.5h6.5v-6.5a.75.75 0 0 1 1.5 0v6.5h6.5a.75.75 0 0 1 0 1.5" />
     <path
         android:fillType="evenOdd"
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M19.25 11.25h-6.5v-6.5a.75.75 0 0 0-1.5 0v6.5h-6.5a.75.75 0 0 0 0 1.5h6.5v6.5a.75.75 0 0 0 1.5 0v-6.5h6.5a.75.75 0 0 0 0-1.5" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_grab_vector.xml b/core/res/res/drawable/pointer_grab_vector.xml
index 7d9f048..48c01ce 100644
--- a/core/res/res/drawable/pointer_grab_vector.xml
+++ b/core/res/res/drawable/pointer_grab_vector.xml
@@ -22,6 +22,6 @@
         android:fillColor="#000000"
         android:pathData="M20.442 7.562a2 2 0 0 0-2-2c-.366 0-.705.106-1 .277V4.686a2 2 0 0 0-2-2 2 2 0 0 0-1.004.279 1.995 1.995 0 0 0-3.986-.06 2 2 0 0 0-1.006-.28 2 2 0 0 0-2 2v6.501l-.247-.253a2.216 2.216 0 0 0-3.178 0 2.286 2.286 0 0 0 0 3.186l5.106 5.224q.063.061.131.118l-.001.001a6.58 6.58 0 0 0 4.624 1.901c3.587 0 6.565-2.906 6.565-6.516q0-.105-.004-.21m-6.561 5.727a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-5.106-5.224a1.286 1.286 0 0 1 0-1.788 1.215 1.215 0 0 1 1.747 0l1.962 2.008V4.625a1 1 0 0 1 2 0v5.833q.463-.362.996-.623V3a1 1 0 0 1 2 0v6.29a6 6 0 0 1 1 .011V4.686a1 1 0 0 1 2 0v5.21c.357.185.693.408 1 .663V7.562a1 1 0 0 1 2 0v7.019h.001-.001q.004.104.004.207c.001 3.046-2.518 5.516-5.564 5.516" />
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="?attr/pointerIconVectorFillInverse"
         android:pathData="M19.442 14.581V7.562a1 1 0 0 0-2 0v2.997a5.7 5.7 0 0 0-1-.663v-5.21a1 1 0 0 0-2 0v4.615a5.5 5.5 0 0 0-1-.011V3a1 1 0 0 0-2 0v6.835a5.6 5.6 0 0 0-.996.623V4.625a1 1 0 0 0-2 0v8.955l-1.962-2.008a1.215 1.215 0 0 0-1.747 0 1.286 1.286 0 0 0 0 1.788l5.106 5.224q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.046 0 5.565-2.469 5.565-5.516z" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_grabbing_vector.xml b/core/res/res/drawable/pointer_grabbing_vector.xml
index 9c96103..ad9f86c 100644
--- a/core/res/res/drawable/pointer_grabbing_vector.xml
+++ b/core/res/res/drawable/pointer_grabbing_vector.xml
@@ -22,6 +22,6 @@
         android:fillColor="#000000"
         android:pathData="M19.485 12.622V8.508a2 2 0 0 0-3.12-1.657 1.993 1.993 0 0 0-2.99-1.006 1.99 1.99 0 0 0-1.886-1.361c-.903 0-1.658.603-1.906 1.425a2 2 0 0 0-3.09 1.674v2.206a2.2 2.2 0 0 0-2.159.586 2.285 2.285 0 0 0 0 3.185l3.847 3.936q.063.061.13.118l-.001.001a6.58 6.58 0 0 0 4.624 1.902c3.586 0 6.563-2.905 6.563-6.514a5 5 0 0 0-.012-.381m-6.55 5.895a5.58 5.58 0 0 1-3.922-1.613 1 1 0 0 1-.117-.106l-3.847-3.936c-.482-.494-.482-1.294 0-1.787s1.265-.494 1.747 0l.697.713V7.583a1 1 0 0 1 2 0v1.096q.463-.364.997-.625v-1.57a1 1 0 0 1 2 0v1.022a5.5 5.5 0 0 1 .996.009v-.007a1 1 0 0 1 2 0v.599q.537.277 1 .66v-.259a1 1 0 0 1 2 0v4.115q.013.189.013.38c-.001 3.045-2.518 5.514-5.564 5.514" />
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="?attr/pointerIconVectorFillInverse"
         android:pathData="M18.485 12.622V8.508a1 1 0 0 0-2 0v.259a5.6 5.6 0 0 0-1-.66v-.599a1 1 0 0 0-2 0v.008a5.6 5.6 0 0 0-.996-.009V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514a5 5 0 0 0-.012-.381" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_hand_vector.xml b/core/res/res/drawable/pointer_hand_vector.xml
index 79792f8..a06dc08 100644
--- a/core/res/res/drawable/pointer_hand_vector.xml
+++ b/core/res/res/drawable/pointer_hand_vector.xml
@@ -22,6 +22,6 @@
         android:fillColor="#000000"
         android:pathData="M20.492 15.197v-4.198A1.995 1.995 0 0 0 18.5 9.001c-.413 0-.797.126-1.115.342a1.99 1.99 0 0 0-1.873-1.341c-.411 0-.792.125-1.109.339a1.99 1.99 0 0 0-1.879-1.361c-.363 0-.699.105-.992.275V3.998A1.99 1.99 0 0 0 9.542 2c-1.1 0-1.992.895-1.992 1.998v7.831l-.242-.249a2.2 2.2 0 0 0-3.164 0 2.29 2.29 0 0 0 0 3.183l5.084 5.219q.063.061.13.118l-.001.001A6.54 6.54 0 0 0 13.963 22c3.572 0 6.537-2.903 6.537-6.509q0-.148-.008-.294m-6.529 5.804a5.55 5.55 0 0 1-3.906-1.611 1 1 0 0 1-.117-.106l-5.084-5.219a1.286 1.286 0 0 1 0-1.786 1.21 1.21 0 0 1 1.74 0l1.95 2.002V3.998c0-.552.446-.999.996-.999s.996.447.996.999v7.17l.011-.007a.495.495 0 0 0 .989-.037V8.939a.992.992 0 0 1 1.984.039v.796l-.007 1.386a.5.5 0 0 0 .495.502h.003a.5.5 0 0 0 .498-.497l.006-1.157h.001V10a.997.997 0 1 1 1.991 0v.601l.004.003v1.02q.001.107.042.199a.5.5 0 0 0 .153.187l.031.021a.5.5 0 0 0 .231.083c.014.001.026.008.04.008a.5.5 0 0 0 .498-.5v-.642a.996.996 0 0 1 .993-.98c.55 0 .996.447.996.999v4.199a6 6 0 0 1 .008.293c-.001 3.043-2.509 5.51-5.542 5.51" />
     <path
-        android:fillColor="#FFFFFF"
+        android:fillColor="?attr/pointerIconVectorFillInverse"
         android:pathData="M19.496 10.999A.997.997 0 0 0 18.5 10a.995.995 0 0 0-.992.98v.644a.5.5 0 0 1-.498.5c-.014 0-.026-.007-.04-.008a.493.493 0 0 1-.457-.491v-1.02l-.004-.003V10c0-.552-.446-.999-.996-.999s-.996.447-.996.999v.008h-.001l-.005 1.003-.001.154a.5.5 0 0 1-.498.497h-.003a.5.5 0 0 1-.495-.502l.001-.159.006-1.227v-.796a.997.997 0 0 0-.996-.999.993.993 0 0 0-.988.96v2.185a.496.496 0 0 1-.989.037l-.011.007v-7.17a.997.997 0 0 0-.996-.999.997.997 0 0 0-.996.999V14.28l-1.95-2.002a1.21 1.21 0 0 0-1.74 0 1.286 1.286 0 0 0 0 1.786l5.084 5.219q.056.057.117.106A5.54 5.54 0 0 0 13.962 21c3.033 0 5.541-2.467 5.541-5.51a6 6 0 0 0-.008-.293z" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_handwriting_vector.xml b/core/res/res/drawable/pointer_handwriting_vector.xml
index 09f3e31..8497592 100644
--- a/core/res/res/drawable/pointer_handwriting_vector.xml
+++ b/core/res/res/drawable/pointer_handwriting_vector.xml
@@ -23,6 +23,6 @@
         <path android:fillColor="#FFFFFF" android:pathData="m16.431 7.64-6.29 6.29 1.43 1.43 6.29-6.29-1.42-1.43z" />
     </group>
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M5 4c-.55 0-1 .45-1 1v14c0 .55.45 1 1 1s1-.45 1-1V5c0-.55-.45-1-1-1m14.41 3.51-1.42-1.42c-.39-.39-.9-.59-1.41-.59h-.01c-.51 0-1.02.2-1.41.59L8 13.25v4.25h4.25l7.16-7.16c.78-.78.78-2.05 0-2.83m-7.839 7.85-1.43-1.43 6.29-6.29h.01l1.42 1.43z" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_help_vector.xml b/core/res/res/drawable/pointer_help_vector.xml
index 6b7fd9f..07970fb 100644
--- a/core/res/res/drawable/pointer_help_vector.xml
+++ b/core/res/res/drawable/pointer_help_vector.xml
@@ -19,12 +19,12 @@
     android:viewportHeight="24"
     android:viewportWidth="24">
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M16.339 11.18 6.773 4.017a1.78 1.78 0 0 0-1.072-.365c-.274 0-.551.065-.813.196a1.77 1.77 0 0 0-.994 1.611l.009 11.951c0 .903.59 1.457 1.142 1.674.2.078.433.128.678.128.434 0 .906-.155 1.298-.578l2.97-3.193a1.8 1.8 0 0 1 1.126-.563l4.335-.467c.899-.097 1.387-.741 1.544-1.312.157-.573.066-1.377-.657-1.919" />
     <path
         android:fillColor="#FFFFFF"
         android:pathData="M16.94 10.38 7.37 3.22a2.77 2.77 0 0 0-2.93-.27A2.75 2.75 0 0 0 2.9 5.46l.01 11.95a2.79 2.79 0 0 0 2.82 2.8c.78 0 1.5-.32 2.03-.9l2.97-3.19a.8.8 0 0 1 .5-.25l4.34-.46a2.76 2.76 0 0 0 2.4-2.05 2.8 2.8 0 0 0-1.02-2.98zM17 13.1a1.77 1.77 0 0 1-1.55 1.31l-4.33.47a1.8 1.8 0 0 0-1.13.56l-2.97 3.2c-.4.42-.86.57-1.3.57-.24 0-.48-.05-.68-.13a1.77 1.77 0 0 1-1.14-1.67V5.46a1.81 1.81 0 0 1 1.8-1.8c.38 0 .75.11 1.07.36l9.57 7.16c.72.54.81 1.35.66 1.92zm2.64-10.83a2.5 2.5 0 0 0-1.84-.72 3 3 0 0 0-2.83 1.93l-.39.94.96.37.86.32.12.05-.02.03c-.22.4-.3.82-.3 1.33v.94a1.56 1.56 0 0 0 .4 1.47 1.54 1.54 0 0 0 2.24.01 1.55 1.55 0 0 0 .28-1.84v-.52c0-.1.02-.17.03-.25l.16-.15c.32-.25.6-.56.78-.93.18-.37.26-.76.26-1.16 0-.68-.21-1.32-.7-1.82zm-1.5 5.96a.55.55 0 0 1-.82 0 .56.56 0 0 1-.17-.4c0-.16.06-.3.17-.4a.55.55 0 0 1 .41-.18c.15 0 .28.06.4.17a.55.55 0 0 1 0 .81zm1.05-3.42c-.1.22-.28.42-.52.6-.26.22-.42.42-.47.6-.05.18-.08.37-.08.57l-.93-.06c0-.38.07-.62.19-.86.13-.24.3-.46.54-.66.17-.13.3-.28.4-.43s.14-.3.14-.46c0-.2-.08-.37-.22-.5s-.31-.17-.52-.17c-.2 0-.39.06-.56.18-.17.13-.3.31-.4.56l-.87-.33a2.03 2.03 0 0 1 1.91-1.3c.48 0 .86.14 1.13.42.28.28.41.65.41 1.12 0 .26-.05.5-.15.72z" />
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M17.73 7.254a.55.55 0 0 0-.407.169.55.55 0 0 0-.169.407q0 .225.169.401a.55.55 0 0 0 .808 0 .56.56 0 0 0 .175-.413.53.53 0 0 0-.175-.394.56.56 0 0 0-.401-.17m1.202-4.288q-.413-.42-1.126-.419-.651 0-1.164.357a2.1 2.1 0 0 0-.751.945l.864.326q.15-.363.407-.551a.93.93 0 0 1 .557-.188q.313 0 .526.182c.213.182.213.286.213.495q0 .226-.144.457a1.4 1.4 0 0 1-.394.432q-.35.3-.538.657c-.125.238-.187.485-.187.86l.926.06q0-.3.081-.57t.469-.595q.363-.276.519-.601t.156-.726q-.002-.701-.414-1.121" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml
index d1aea9e..32c56b6 100644
--- a/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_horizontal_double_arrow_vector.xml
@@ -22,6 +22,6 @@
         android:fillColor="#FFFFFF"
         android:pathData="m19.963 10.185-3.065-1.758c-1.327-.761-2.96.14-3.072 1.633h-3.651c-.113-1.492-1.746-2.394-3.072-1.633l-3.065 1.758c-1.383.793-1.383 2.786 0 3.579l3.065 1.758c1.311.752 2.918-.12 3.065-1.581h3.666c.147 1.46 1.754 2.333 3.065 1.581l3.065-1.758c1.382-.793 1.382-2.786-.001-3.579m-.498 2.712L16.4 14.655a1.065 1.065 0 0 1-1.596-.922v-.791H9.195v.791c0 .818-.886 1.33-1.596.922l-3.065-1.758a1.063 1.063 0 0 1 0-1.845l3.065-1.758a1.065 1.065 0 0 1 1.596.922v.843h5.609v-.843c0-.818.886-1.33 1.596-.922l3.065 1.758a1.063 1.063 0 0 1 0 1.845" />
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M19.465 11.052 16.4 9.294a1.065 1.065 0 0 0-1.596.922v.843H9.195v-.843c0-.818-.886-1.33-1.596-.922l-3.065 1.758a1.063 1.063 0 0 0 0 1.845l3.065 1.758a1.065 1.065 0 0 0 1.596-.922v-.791h5.609v.791c0 .818.886 1.33 1.596.922l3.065-1.758a1.063 1.063 0 0 0 0-1.845" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_nodrop_vector.xml b/core/res/res/drawable/pointer_nodrop_vector.xml
index 3a38bab..6108e96 100644
--- a/core/res/res/drawable/pointer_nodrop_vector.xml
+++ b/core/res/res/drawable/pointer_nodrop_vector.xml
@@ -19,8 +19,8 @@
     android:viewportHeight="24"
     android:viewportWidth="24">
     <group>
-        <path android:fillColor="#FFFFFF" android:pathData="M17.5 1.953c-2.108 0-3.869 1.449-4.382 3.398a4.5 4.5 0 0 0-.165 1.148c0 .343.045.674.117.995-.045-.001-.09-.007-.135-.007q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.69a4.5 4.5 0 0 0 1-.366c1.51-.739 2.562-2.275 2.562-4.066A4.55 4.55 0 0 0 17.5 1.953m0 8.047C15.57 10 14 8.43 14 6.5S15.57 3 17.5 3 21 4.57 21 6.5 19.43 10 17.5 10" />
-        <path android:fillColor="#FFFFFF" android:pathData="M17.5 4c-.493 0-.95.148-1.337.395l3.442 3.442C19.852 7.45 20 6.993 20 6.5 20 5.121 18.879 4 17.5 4M15 6.5C15 7.879 16.121 9 17.5 9c.525 0 1.011-.164 1.413-.441l-3.472-3.472A2.5 2.5 0 0 0 15 6.5" />
+        <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M17.5 1.953c-2.108 0-3.869 1.449-4.382 3.398a4.5 4.5 0 0 0-.165 1.148c0 .343.045.674.117.995-.045-.001-.09-.007-.135-.007q-.225 0-.446.018V6.484a1 1 0 0 0-2 0v1.57a5.7 5.7 0 0 0-.997.625V7.583a1 1 0 0 0-2 0v4.205l-.697-.713c-.482-.494-1.265-.494-1.747 0s-.482 1.294 0 1.787l3.847 3.936q.056.057.117.106a5.58 5.58 0 0 0 3.922 1.613c3.045 0 5.563-2.469 5.563-5.514q0-.192-.013-.38v-1.69a4.5 4.5 0 0 0 1-.366c1.51-.739 2.562-2.275 2.562-4.066A4.55 4.55 0 0 0 17.5 1.953m0 8.047C15.57 10 14 8.43 14 6.5S15.57 3 17.5 3 21 4.57 21 6.5 19.43 10 17.5 10" />
+        <path android:fillColor="?attr/pointerIconVectorFillInverse" android:pathData="M17.5 4c-.493 0-.95.148-1.337.395l3.442 3.442C19.852 7.45 20 6.993 20 6.5 20 5.121 18.879 4 17.5 4M15 6.5C15 7.879 16.121 9 17.5 9c.525 0 1.011-.164 1.413-.441l-3.472-3.472A2.5 2.5 0 0 0 15 6.5" />
     </group>
     <path
         android:fillColor="#000000"
diff --git a/core/res/res/drawable/pointer_text_vector.xml b/core/res/res/drawable/pointer_text_vector.xml
index 9e44f28..a147273 100644
--- a/core/res/res/drawable/pointer_text_vector.xml
+++ b/core/res/res/drawable/pointer_text_vector.xml
@@ -19,7 +19,7 @@
     android:viewportHeight="24"
     android:viewportWidth="24">
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M12 3c-.551 0-1 .448-1 1v14a1.001 1.001 0 0 0 2 0V4c0-.552-.449-1-1-1" />
     <path
         android:fillColor="#FFFFFF"
diff --git a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml
index e5d5301..7f95207 100644
--- a/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_top_left_diagonal_double_arrow_vector.xml
@@ -23,6 +23,6 @@
         android:pathData="m18.896 16.365-.924-3.41c-.398-1.467-2.169-1.985-3.305-1.035L12.08 9.333c.952-1.136.434-2.908-1.034-3.306l-3.41-.924c-1.539-.416-2.948.993-2.532 2.532l.924 3.41c.398 1.468 2.17 1.986 3.306 1.034l2.586 2.586c-.953 1.136-.435 2.91 1.033 3.307l3.41.924c1.54.417 2.949-.992 2.533-2.531m-2.27 1.566-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476L6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.477l.924 3.41a1.062 1.062 0 0 1-1.304 1.303" />
     <path
         android:fillType="evenOdd"
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M6.07 7.373a1.063 1.063 0 0 1 1.304-1.304l3.41.924a1.065 1.065 0 0 1 .476 1.781l-.578.578 3.966 3.966.577-.577a1.066 1.066 0 0 1 1.781.476l.924 3.41a1.063 1.063 0 0 1-1.304 1.304l-3.41-.924a1.065 1.065 0 0 1-.476-1.781l.579-.579-3.966-3.966-.579.579a1.066 1.066 0 0 1-1.781-.476z" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml
index e6f7aaf..8a33715 100644
--- a/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_top_right_diagonal_double_arrow_vector.xml
@@ -22,6 +22,6 @@
         android:fillColor="#FFFFFF"
         android:pathData="m16.365 5.104-3.41.924c-1.468.398-1.986 2.171-1.033 3.307l-2.586 2.586c-1.136-.952-2.909-.434-3.306 1.034l-.924 3.41c-.417 1.539.992 2.948 2.531 2.531l3.41-.924c1.468-.398 1.986-2.17 1.034-3.306l2.587-2.587c1.136.951 2.908.432 3.305-1.035l.924-3.41c.415-1.538-.994-2.947-2.532-2.53m1.565 2.269-.924 3.41a1.065 1.065 0 0 1-1.781.476l-.577-.577-3.966 3.966.578.578a1.066 1.066 0 0 1-.476 1.781l-3.41.924a1.063 1.063 0 0 1-1.304-1.304l.924-3.41a1.066 1.066 0 0 1 1.781-.477l.578.578 3.966-3.966-.579-.579a1.066 1.066 0 0 1 .476-1.781l3.41-.924a1.063 1.063 0 0 1 1.304 1.305" />
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="m16.626 6.069-3.41.924a1.065 1.065 0 0 0-.476 1.781l.579.579-3.966 3.966-.579-.579a1.066 1.066 0 0 0-1.781.477l-.924 3.41a1.063 1.063 0 0 0 1.304 1.304l3.41-.924a1.065 1.065 0 0 0 .476-1.781l-.578-.578 3.966-3.966.577.577a1.066 1.066 0 0 0 1.781-.476l.924-3.41a1.062 1.062 0 0 0-1.303-1.304" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml
index 6ffcfef..889372c 100644
--- a/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml
+++ b/core/res/res/drawable/pointer_vertical_double_arrow_vector.xml
@@ -22,6 +22,6 @@
         android:fillColor="#FFFFFF"
         android:pathData="M13.945 13.829V10.17c1.476-.131 2.363-1.75 1.606-3.069l-1.758-3.065c-.793-1.383-2.786-1.383-3.579 0L8.455 7.102c-.757 1.319.131 2.939 1.607 3.069v3.658c-1.477.13-2.364 1.75-1.607 3.069l1.758 3.065c.793 1.383 2.786 1.383 3.579 0l1.758-3.065c.758-1.319-.129-2.938-1.605-3.069m.739 2.572-1.758 3.065a1.063 1.063 0 0 1-1.845 0l-1.758-3.065a1.065 1.065 0 0 1 .922-1.596h.818v-5.61h-.818c-.818 0-1.33-.886-.922-1.596l1.758-3.065a1.063 1.063 0 0 1 1.845 0l1.758 3.065a1.065 1.065 0 0 1-.922 1.596h-.817v5.609h.817c.817.001 1.329.886.922 1.597" />
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M13.761 14.805h-.817v-5.61h.817c.818 0 1.33-.886.922-1.596l-1.758-3.065a1.063 1.063 0 0 0-1.845 0L9.323 7.599c-.407.71.104 1.596.922 1.596h.818v5.609h-.818c-.818 0-1.33.886-.922 1.596l1.758 3.065a1.063 1.063 0 0 0 1.845 0l1.758-3.065a1.065 1.065 0 0 0-.923-1.595" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_vertical_text_vector.xml b/core/res/res/drawable/pointer_vertical_text_vector.xml
index 72f40cc..9238f94 100644
--- a/core/res/res/drawable/pointer_vertical_text_vector.xml
+++ b/core/res/res/drawable/pointer_vertical_text_vector.xml
@@ -19,7 +19,7 @@
     android:viewportHeight="24"
     android:viewportWidth="24">
     <path
-        android:fillColor="#000000"
+        android:fillColor="?attr/pointerIconVectorFill"
         android:pathData="M19 11H5a1 1 0 0 0 0 2h14a1 1 0 0 0 0-2" />
     <path
         android:fillColor="#FFFFFF"
diff --git a/core/res/res/drawable/pointer_zoom_in_vector.xml b/core/res/res/drawable/pointer_zoom_in_vector.xml
index 8921666..a7f56c2 100644
--- a/core/res/res/drawable/pointer_zoom_in_vector.xml
+++ b/core/res/res/drawable/pointer_zoom_in_vector.xml
@@ -23,7 +23,7 @@
         <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-2v2a.5.5 0 0 1-1 0v-2h-2a.5.5 0 0 1 0-1h2V7a.5.5 0 0 1 1 0v2h2a.5.5 0 0 1 0 1" />
     </group>
     <group>
-        <path android:fillColor="#000000" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" />
-        <path android:fillColor="#000000" android:pathData="M13.012 9h-2V7a.5.5 0 0 0-1 0v2h-2a.5.5 0 0 0 0 1h2v2a.5.5 0 0 0 1 0v-2h2a.5.5 0 0 0 0-1" />
+        <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" />
+        <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="M13.012 9h-2V7a.5.5 0 0 0-1 0v2h-2a.5.5 0 0 0 0 1h2v2a.5.5 0 0 0 1 0v-2h2a.5.5 0 0 0 0-1" />
     </group>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/pointer_zoom_out_vector.xml b/core/res/res/drawable/pointer_zoom_out_vector.xml
index 815ce0e..e46b978 100644
--- a/core/res/res/drawable/pointer_zoom_out_vector.xml
+++ b/core/res/res/drawable/pointer_zoom_out_vector.xml
@@ -23,7 +23,7 @@
         <path android:fillColor="#FFFFFF" android:pathData="M10.55 5a4.546 4.546 0 1 0 0 9.093 4.546 4.546 0 0 0 0-9.093m2.462 5h-5a.5.5 0 0 1 0-1h5a.5.5 0 0 1 0 1" />
     </group>
     <group>
-        <path android:fillColor="#000000" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" />
-        <path android:fillColor="#000000" android:pathData="M13.012 9h-5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1" />
+        <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="m19.736 18.003-4.194-4.22a6.547 6.547 0 1 0-1.382 1.226l4.268 4.294a.923.923 0 0 0 1.308-1.3m-9.186-3.91A4.546 4.546 0 1 1 10.549 5a4.546 4.546 0 0 1 .001 9.093" />
+        <path android:fillColor="?attr/pointerIconVectorFill" android:pathData="M13.012 9h-5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1" />
     </group>
 </vector>
\ No newline at end of file
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
index 776a35d..96f0909 100644
--- a/core/res/res/layout/floating_popup_container.xml
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -24,4 +24,4 @@
     android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
     android:focusable="true"
     android:focusableInTouchMode="true"
-    android:background="?attr/floatingToolbarPopupBackgroundDrawable"/>
+    android:background="@drawable/floating_popup_background"/>
diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml
index e4c2a34..0b3861c 100644
--- a/core/res/res/layout/floating_popup_menu_button.xml
+++ b/core/res/res/layout/floating_popup_menu_button.xml
@@ -16,6 +16,7 @@
 */
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:orientation="horizontal"
@@ -53,7 +54,7 @@
         android:ellipsize="end"
         android:fontFamily="@*android:string/config_bodyFontFamily"
         android:textSize="@dimen/floating_toolbar_text_size"
-        android:textColor="?attr/floatingToolbarForegroundColor"
+        android:textColor="?androidprv:attr/materialColorOnSurface"
         android:background="@null"
         android:focusable="false"
         android:focusableInTouchMode="false"
diff --git a/core/res/res/layout/floating_popup_overflow_button.xml b/core/res/res/layout/floating_popup_overflow_button.xml
index 12e2000..a51836b 100644
--- a/core/res/res/layout/floating_popup_overflow_button.xml
+++ b/core/res/res/layout/floating_popup_overflow_button.xml
@@ -16,6 +16,7 @@
 */
 -->
 <ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/overflow"
     android:layout_width="@dimen/floating_toolbar_menu_image_button_width"
     android:layout_height="@dimen/floating_toolbar_height"
@@ -25,4 +26,4 @@
     android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding"
     android:scaleType="centerInside"
     android:background="?attr/actionBarItemBackground"
-    android:tint="?attr/floatingToolbarForegroundColor" />
+    android:tint="?androidprv:attr/materialColorOnSurface" />
diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml
index 9fd615d..2cde9e6 100644
--- a/core/res/res/layout/immersive_mode_cling.xml
+++ b/core/res/res/layout/immersive_mode_cling.xml
@@ -14,79 +14,67 @@
      limitations under the License.
 -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        android:theme="@android:style/Theme.DeviceDefault.DayNight"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="?android:attr/colorAccent"
+        android:background="@android:drawable/immersive_cling_bg"
         android:gravity="center_vertical"
-        android:paddingBottom="24dp">
+        android:padding="24dp">
 
-    <FrameLayout
-            android:id="@+id/immersive_cling_chevron"
-            android:layout_width="76dp"
-            android:layout_height="76dp"
-            android:layout_marginTop="-24dp"
-            android:layout_centerHorizontal="true">
-
-        <ImageView
-                android:id="@+id/immersive_cling_back_bg_light"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:scaleType="center"
-                android:src="@drawable/immersive_cling_light_bg_circ" />
-
-        <ImageView
-                android:id="@+id/immersive_cling_back_bg"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:scaleType="center"
-                android:src="@drawable/immersive_cling_bg_circ" />
-
-        <ImageView
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:paddingTop="8dp"
-                android:scaleType="center"
-                android:src="@drawable/ic_expand_more_48dp"
-                android:tint="?android:attr/colorAccent"/>
-    </FrameLayout>
+    <!-- The top margin of this icon can be adjusted to push the content down to prevent overlapping
+         with the display cutout. -->
+    <ImageView
+            android:id="@+id/immersive_cling_icon"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:layout_centerHorizontal="true"
+            android:scaleType="fitXY"
+            android:src="@drawable/ic_swipe_down"
+            android:tint="?android:attr/colorAccent"
+            android:tintMode="src_in" />
 
     <TextView
             android:id="@+id/immersive_cling_title"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_below="@id/immersive_cling_chevron"
-            android:paddingEnd="48dp"
-            android:paddingStart="48dp"
-            android:paddingTop="40dp"
+            android:layout_below="@id/immersive_cling_icon"
+            android:layout_marginTop="20dp"
+            android:gravity="center_horizontal"
             android:text="@string/immersive_cling_title"
-            android:textColor="@android:color/white"
-            android:textSize="24sp" />
+            android:textColor="?androidprv:attr/materialColorOnSurface"
+            android:textSize="24sp"
+            android:fontFamily="google-sans" />
 
     <TextView
             android:id="@+id/immersive_cling_description"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_below="@id/immersive_cling_title"
-            android:paddingEnd="48dp"
-            android:paddingStart="48dp"
-            android:paddingTop="12.6dp"
+            android:paddingTop="14dp"
+            android:gravity="center_horizontal"
             android:text="@string/immersive_cling_description"
-            android:textColor="@android:color/white"
-            android:textSize="16sp" />
+            android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+            android:textSize="14sp"
+            android:fontFamily="google-sans" />
 
     <Button
             android:id="@+id/ok"
-            style="@style/Widget.Material.Button.Borderless"
+            style="@style/Widget.Material.Button.Borderless.Colored"
+            android:background="@drawable/immersive_cling_btn_bg"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentEnd="true"
             android:layout_below="@+id/immersive_cling_description"
-            android:layout_marginEnd="40dp"
-            android:layout_marginTop="18dp"
-            android:paddingEnd="8dp"
-            android:paddingStart="8dp"
+            android:layout_marginTop="24dp"
+            android:paddingStart="18dp"
+            android:paddingEnd="18dp"
+            android:minWidth="48dp"
+            android:minHeight="48dp"
             android:text="@string/immersive_cling_positive"
-            android:textColor="@android:color/white"
-            android:textSize="14sp" />
-
+            android:textColor="?androidprv:attr/materialColorOnPrimary"
+            android:textAllCaps="false"
+            android:textSize="14sp"
+            android:textFontWeight="500"
+            android:fontFamily="google-sans" />
 </RelativeLayout>
diff --git a/core/res/res/layout/subscription_item_layout.xml b/core/res/res/layout/subscription_item_layout.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/layout/text_edit_suggestion_container_material.xml b/core/res/res/layout/text_edit_suggestion_container_material.xml
index 34e7bc8..d6e1e9d 100644
--- a/core/res/res/layout/text_edit_suggestion_container_material.xml
+++ b/core/res/res/layout/text_edit_suggestion_container_material.xml
@@ -23,7 +23,7 @@
         android:id="@+id/suggestionWindowContainer"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="?android:attr/floatingToolbarPopupBackgroundDrawable"
+        android:background="@drawable/floating_popup_background"
         android:elevation="@android:dimen/text_edit_floating_toolbar_elevation"
         android:layout_margin="@android:dimen/text_edit_floating_toolbar_margin"
         android:orientation="vertical"
diff --git a/core/res/res/values-af/donottranslate-cldr.xml b/core/res/res/values-af/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index ca6a384..0c6d49e 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN is te kort. Moet ten minste 4 syfers wees."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Probeer later weer"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Bekyk tans volskerm"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Swiep van bo na onder as jy wil uitgaan."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Het dit"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Draai vir ’n beter aansig"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Maak <xliff:g id="NAME">%s</xliff:g> in volskerm oop vir ’n beter aansig"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaat ruimte"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenskaplik"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaat ruimte"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitiewe kennisgewinginhoud is versteek"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Appinhoud is weens sekuriteit van skermdeling verberg"</string>
diff --git a/core/res/res/values-am/donottranslate-cldr.xml b/core/res/res/values-am/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 218b13a..39152fe 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"ፒን በጣም አጭር ነው። ቢያንስ 4 አሃዝ መሆን አለበት።"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"ቆይተው እንደገና ይሞክሩ"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"ሙሉ ገፅ በማሳየት ላይ"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"ለመውጣት፣ ከላይ ወደታች ጠረግ ያድርጉ።"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ገባኝ"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ለተሻለ ዕይታ ያሽከርክሩ"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ለተሻለ ዕይታ <xliff:g id="NAME">%s</xliff:g>ን በሙሉ ገጽ ዕይታ ይክፈቱ"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"የግል ቦታ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"አባዛ"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"የጋራ"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"የግል ቦታ"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"አደገኛ የማሳወቂያ ይዘት ተደብቋል"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ለደኅንነት ሲባል የመተግበሪያ ይዘት ከማያ ገጽ ማጋራት ተደብቋል"</string>
diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ar/donottranslate-cldr.xml b/core/res/res/values-ar/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d0e28b7..3d066d9 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1887,7 +1887,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"رقم التعريف الشخصي أقصر مما يلزم، يجب ألا يقل عن ٤ أرقام. "</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"أعد المحاولة لاحقًا"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"جارٍ العرض بملء الشاشة"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"للخروج، مرر بسرعة من أعلى إلى أسفل."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"حسنًا"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"يمكنك تدوير الجهاز لرؤية شاشة معاينة الكاميرا بشكل أوضح."</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"عليك فتح \"<xliff:g id="NAME">%s</xliff:g>\" في وضع ملء الشاشة لرؤية شاشة معاينة الكاميرا بشكل أوضح."</string>
@@ -2411,6 +2412,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"المساحة الخاصة"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"نسخة طبق الأصل"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ملف شخصي مشترك"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"المساحة الخاصّة"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"تم إخفاء المحتوى الحساس في الإشعار"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"تم إخفاء محتوى التطبيق بعد تفعيل ميزة \"مشاركة الشاشة\" للحفاظ على أمانك"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 42b0af9..b9c072b 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1865,7 +1865,7 @@
     <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string>
     <string name="mediasize_japanese_l" msgid="1326765321473431817">"L"</string>
     <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"অজ্ঞাত প\'ৰ্ট্ৰেইট"</string>
-    <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কেইপ"</string>
+    <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কে’প"</string>
     <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"বাতিল কৰা হ’ল"</string>
     <string name="write_fail_reason_cannot_write" msgid="432118118378451508">"সমল লিখাত আসোঁৱাহ"</string>
     <string name="reason_unknown" msgid="5599739807581133337">"অজ্ঞাত"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"পিনটো অতি চুটি। কমেও ৪টা সংখ্যাৰ হ\'ব লাগিব।"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"পাছত আকৌ চেষ্টা কৰক"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"স্ক্ৰীন পূৰ্ণৰূপত চাই আছে"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"বাহিৰ হ\'বলৈ ওপৰৰপৰা তললৈ ছোৱাইপ কৰক।"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"বুজি পালোঁ"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ভালকৈ চাবলৈ ঘূৰাওক"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ভালকৈ চাবলৈ <xliff:g id="NAME">%s</xliff:g> পূৰ্ণ স্ক্ৰীনত খোলক"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্ৰাইভেট স্পে’চ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্ল’ন"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"সম্প্ৰদায়ৰ সৈতে জড়িত"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্ৰাইভেট স্পে’চ"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল জাননী লুকুওৱা হৈছে"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"সুৰক্ষাৰ বাবে এপৰ সমল স্ক্ৰীণ শ্বেয়াৰ কৰাৰ পৰা লুকুৱাই ৰখা হৈছে"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 5633c06..32210f7 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PİN çox qısadır. Ən azı 4 rəqəm olmalıdır."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Daha sonra yenidən yoxlayın."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Tam ekrana baxış"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Çıxmaq üçün yuxarıdan aşağı sürüşdürün."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Anladım"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Daha yaxşı görünüş üçün fırladın"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Yaxşı görmək üçün <xliff:g id="NAME">%s</xliff:g> tətbiqini tam ekranda açın"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Məxfi sahə"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kommunal"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Şəxsi sahə"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Həssas bildiriş kontenti gizlədildi"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Güvənlik üçün tətbiq kontenti ekran paylaşımından gizlədildi"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index e38706e..ac01883 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN je prekratak. Mora da ima bar 4 cifre."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Probajte ponovo kasnije"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Prikazuje se ceo ekran"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Da biste izašli, prevucite nadole odozgo."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Važi"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotirajte radi boljeg prikaza"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otvorite aplikaciju <xliff:g id="NAME">%s</xliff:g> preko celog ekrana da biste bolje videli"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatan prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonirano"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatan prostor"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Osetljiv sadržaj obaveštenja je skriven"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je skriven za deljenje sadržaja ekrana zbog bezbednosti"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 25ae5a7..cbcc90a 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1885,7 +1885,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-код занадта кароткі. Павінен змяшчаць не менш за 4 лічбы."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Паўтарыце спробу пазней"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Прагляд у поўнаэкранным рэжыме"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Для выхаду правядзіце зверху ўніз."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Зразумела"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Павярнуць для лепшага прагляду"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Для больш зручнага прагляду адкрыйце праграму \"<xliff:g id="NAME">%s</xliff:g>\", выкарыстоўваючы поўнаэкранны рэжым"</string>
@@ -2409,6 +2410,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Прыватная прастора"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Супольны"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Прыватная прастора"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Канфідэнцыяльнае змесціва ў апавяшчэннях схавана"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Змесціва праграмы выключана з абагульвання экрана ў мэтах бяспекі"</string>
diff --git a/core/res/res/values-bg/donottranslate-cldr.xml b/core/res/res/values-bg/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 6b388d8..affa311 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"ПИН кодът е твърде кратък. Трябва да е поне 4 цифри."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Опитайте отново по-късно"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Изглед на цял екран"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"За изход плъзнете пръст надолу от горната част."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Разбрах"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Завъртете за по-добър изглед"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Отворете <xliff:g id="NAME">%s</xliff:g> на цял екран за по-добър изглед"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частно пространство"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониране"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Общи"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частно пространство"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Деликатното съдържание в известието е скрито"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Съдържанието на приложението е скрито от функцията за споделяне на екрана от съображения за сигурност"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 3f338cc..889de67 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"পিন খুবই ছোট৷ এটিকে কমপক্ষে ৪ সংখ্যার হতে হবে৷"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"পরে আবার চেষ্টা করুন"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"পূর্ণ স্ক্রিনে দেখা হচ্ছে"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"প্রস্থান করতে উপর থেকে নিচের দিকে সোয়াইপ করুন"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"বুঝেছি"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"আরও ভাল ক্যামেরা ভিউ পাওয়ার জন্য ঘোরান"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"আরও ভাল ভিউয়ের জন্য ফুল স্ক্রিনে <xliff:g id="NAME">%s</xliff:g> খুলুন"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্রাইভেট স্পেস"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্লোন"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"কমিউনাল"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্রাইভেট স্পেস"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল বিজ্ঞপ্তির কন্টেন্ট লুকানো আছে"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"নিরাপত্তার জন্য স্ক্রিন শেয়ার করা থেকে লুকানো অ্যাপের কন্টেন্ট"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 0438302..af9ac56 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN je prekratak. Mora imati najmanje 4 cifre."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Pokušajte ponovo kasnije."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Prikazuje se cijeli ekran"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Da izađete, prevucite odozgo nadolje."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Razumijem"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotirajte za bolji prikaz"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otvorite aplikaciju <xliff:g id="NAME">%s</xliff:g> preko cijelog ekrana radi boljeg pregleda"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Opće"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Sakriven je osjetljiv sadržaj obavještenja"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je sakriven od dijeljenja ekrana radi sigurnosti"</string>
diff --git a/core/res/res/values-ca/donottranslate-cldr.xml b/core/res/res/values-ca/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 0287a27..66c81e6 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"El PIN és massa curt. Ha de tenir quatre dígits com a mínim."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Torna-ho a provar més tard"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Mode de pantalla completa"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Per sortir, llisca cap avall des de la part superior."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entesos"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira per a una millor visualització"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Obre <xliff:g id="NAME">%s</xliff:g> en pantalla completa per a una millor visualització"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espai privat"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comunitari"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espai privat"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"S\'ha amagat contingut sensible de les notificacions"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contingut de l\'aplicació amagat de la compartició de pantalla per motius de seguretat"</string>
diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4013a03..ad43a95 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1885,7 +1885,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Kód PIN je příliš krátký. Musí mít alespoň čtyři číslice."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Zkuste to znovu později"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Zobrazení celé obrazovky"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Režim ukončíte přejetím prstem shora dolů."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Rozumím"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Otočte obrazovku, abyste lépe viděli"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otevřete aplikaci <xliff:g id="NAME">%s</xliff:g> na celou obrazovku, aby byla lépe vidět"</string>
@@ -2409,6 +2410,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Soukromý prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Komunální"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Soukromý prostor"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivých oznámení je skrytý"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikace je z bezpečnostních důvodů při sdílení obrazovky skryt"</string>
diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 5226d56..e32b0b6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Pinkoden er for kort. Den skal være på mindst 4 tal."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Prøv igen senere"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visning i fuld skærm"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Stryg ned fra toppen for at afslutte."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK, det er forstået"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Roter, og få en bedre visning"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Åbn <xliff:g id="NAME">%s</xliff:g> i fuld skærm for at få en bedre visning"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Fælles"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Følsomt indhold i notifikationen er skjult"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Af sikkerhedsmæssige årsager vises appindhold ikke ved skærmdeling"</string>
diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 949150a..ca858790 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Die PIN ist zu kurz. Sie muss mindestens 4 Ziffern umfassen."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Später erneut versuchen"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vollbildmodus wird aktiviert"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Zum Beenden von oben nach unten wischen"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ok"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Drehen, um die Ansicht zu verbessern"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Du kannst <xliff:g id="NAME">%s</xliff:g> im Vollbildmodus öffnen, um die Ansicht zu verbessern"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Vertrauliches Profil"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeinsam genutzt"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Vertrauliches Profil"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Vertrauliche Benachrichtigungsinhalte ausgeblendet"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-Inhalte werden aus Sicherheitsgründen bei der Bildschirmfreigabe ausgeblendet"</string>
diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 11dac6c..cb1d3b1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Το PIN είναι υπερβολικά μικρό. Πρέπει να έχει μέγεθος τουλάχιστον 4 χαρακτήρων."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Δοκιμάστε ξανά αργότερα"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Προβολή σε πλήρη οθόνη"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Για έξοδο, σύρετε προς τα κάτω από το επάνω μέρος."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Το κατάλαβα"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Περιστρέψτε την οθόνη για καλύτερη προβολή"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Ανοίξτε την εφαρμογή <xliff:g id="NAME">%s</xliff:g> σε πλήρη οθόνη για καλύτερη προβολή"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Κοινόχρηστο"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ιδιωτικός χώρος"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Έγινε απόκρυψη της ειδοποίησης ευαίσθητου περιεχομένου"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Για λόγους ασφάλειας, έγινε απόκρυψη του περιεχομένου της εφαρμογής από την κοινή χρήση οθόνης"</string>
diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 1effe7c..e748648 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN is too short. Must be at least four digits."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Try again later"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 4abc581..b19bdf9 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1883,7 +1883,7 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN is too short. Must be at least 4 digits."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Try again later"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
+    <string name="immersive_cling_description" msgid="2896205051090870978">"To exit, swipe down from the top of your screen"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
@@ -2407,6 +2407,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 425d88c..818614a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN is too short. Must be at least four digits."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Try again later"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 56ff14a..a58f731 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN is too short. Must be at least four digits."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Try again later"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Viewing full screen"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"To exit, swipe down from the top."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Got it"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotate for a better view"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> in full screen for a better view"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index e599c03..cb9bbf5 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1883,7 +1883,7 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎PIN is too short. Must be at least 4 digits.‎‏‎‎‏‎"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎Try again later‎‏‎‎‏‎"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎Viewing full screen‎‏‎‎‏‎"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‎‎‎‎To exit, swipe down from the top.‎‏‎‎‏‎"</string>
+    <string name="immersive_cling_description" msgid="2896205051090870978">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎‎To exit, swipe down from the top of your screen‎‏‎‎‏‎"</string>
     <string name="immersive_cling_positive" msgid="7047498036346489883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‎‏‏‎Got it‎‏‎‎‏‎"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‏‏‎‎Rotate for a better view‎‏‎‎‏‎"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎Open ‎‏‎‎‏‏‎<xliff:g id="NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ in full screen for a better view‎‏‎‎‏‎"</string>
@@ -2407,6 +2407,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎Private space‎‏‎‎‏‎"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎Clone‎‏‎‎‏‎"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‎‎‎‎‏‏‏‎Communal‎‏‎‎‏‎"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‎‎Private space‎‏‎‎‏‎"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎Sensitive notification content hidden‎‏‎‎‏‎"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎App content hidden from screen share for security‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rCO/donottranslate-cldr.xml b/core/res/res/values-es-rCO/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rCR/donottranslate-cldr.xml b/core/res/res/values-es-rCR/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rEC/donottranslate-cldr.xml b/core/res/res/values-es-rEC/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rGT/donottranslate-cldr.xml b/core/res/res/values-es-rGT/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rHN/donottranslate-cldr.xml b/core/res/res/values-es-rHN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rMX/donottranslate-cldr.xml b/core/res/res/values-es-rMX/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rNI/donottranslate-cldr.xml b/core/res/res/values-es-rNI/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rPA/donottranslate-cldr.xml b/core/res/res/values-es-rPA/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rPE/donottranslate-cldr.xml b/core/res/res/values-es-rPE/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rSV/donottranslate-cldr.xml b/core/res/res/values-es-rSV/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f5859bf..94591ea8 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"El PIN es demasiado corto. Debe tener al menos 4 dígitos."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Vuelve a intentar más tarde."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualización en pantalla completa"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Para salir, desliza el dedo hacia abajo desde la parte superior."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira la pantalla para obtener una mejor vista"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abre <xliff:g id="NAME">%s</xliff:g> en pantalla completa para una mejor visualización"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Se ocultó contenido sensible de la notificación"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Se ocultó el contenido de la app durante el uso compartido de la pantalla por motivos de seguridad"</string>
diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index d8b1f59..2634494 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -273,7 +273,7 @@
     <string name="bugreport_option_full_title" msgid="7681035745950045690">"Informe completo"</string>
     <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Utiliza esta opción para que la interferencia del sistema sea mínima cuando el dispositivo no responda o funcione demasiado lento, o bien cuando necesites todas las secciones del informe. No permite introducir más detalles ni hacer más capturas de pantalla."</string>
     <string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{La captura de pantalla para el informe de errores se hará en # segundo.}many{La captura de pantalla para el informe de errores se hará en # segundos.}other{La captura de pantalla para el informe de errores se hará en # segundos.}}"</string>
-    <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Se ha hecho la captura de pantalla con el informe de errores"</string>
+    <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"Captura de pantalla generada con el informe de errores"</string>
     <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"No se ha podido hacer la captura de pantalla con el informe de errores"</string>
     <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"Modo Silencio"</string>
     <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"El sonido está desactivado. Activar"</string>
@@ -717,7 +717,7 @@
     <string name="face_acquired_too_right" msgid="6245286514593540859">"Mueve el teléfono hacia la izquierda"</string>
     <string name="face_acquired_too_left" msgid="9201762240918405486">"Mueve el teléfono hacia la derecha"</string>
     <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Mira de forma más directa al dispositivo."</string>
-    <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se puede detectar tu cara. Sujeta el teléfono a la altura de los ojos."</string>
+    <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se detecta tu cara. Sujeta el teléfono a la altura de los ojos."</string>
     <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"El teléfono se mueve demasiado. Mantenlo quieto."</string>
     <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Vuelve a registrar tu cara."</string>
     <string name="face_acquired_too_different" msgid="4505278456634706967">"Cara no reconocida. Inténtalo de nuevo."</string>
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"El PIN es demasiado corto. Debe tener al menos 4 dígitos."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Reintentar más tarde"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Modo de pantalla completa"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Para salir, desliza el dedo de arriba abajo."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gira la pantalla para verlo mejor"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abre <xliff:g id="NAME">%s</xliff:g> en pantalla completa para verlo mejor"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Común"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Contenido sensible de la notificación oculto"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenido de la aplicación oculto en pantalla compartida por motivos de seguridad"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 77e665a..9c9ff1b 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-kood on liiga lühike. Peab olema vähemalt 4-kohaline."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Proovige hiljem uuesti"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Kuvamine täisekraanil"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Väljumiseks pühkige ülevalt alla."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Selge"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Pöörake parema vaate jaoks"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Parema vaate jaoks avage rakendus <xliff:g id="NAME">%s</xliff:g> täisekraanil"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaatne ruum"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ühine"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaatne ruum"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Märguande delikaatne sisu peideti"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Rakenduse sisu on ekraani jagamises turvalisuse huvides peidetud"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 9bf2a4d..d2f77eb 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -190,7 +190,7 @@
     <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"<xliff:g id="CONTENT_TYPE">%s</xliff:g> gehiegi ezabatzen saiatu zara."</string>
     <string name="low_memory" product="tablet" msgid="5557552311566179924">"Tabletaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
     <string name="low_memory" product="watch" msgid="3479447988234030194">"Erlojuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
-    <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
+    <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuko biltegia beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
     <string name="low_memory" product="default" msgid="2539532364144025569">"Telefonoaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string>
     <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoritate ziurtagiri-emaile bat dago instalatuta}other{Autoritate ziurtagiri-emaile bat baino gehiago daude instalatuta}}"</string>
     <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Hirugarren alderdi ezezagun baten arabera"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PINa laburregia da. Lau digitu izan behar ditu gutxienez."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Saiatu berriro geroago"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Pantaila osoko ikuspegia"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Irteteko, pasatu hatza goitik behera."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ados"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Biratu pantaila ikuspegi hobea lortzeko"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Ireki <xliff:g id="NAME">%s</xliff:g> pantaila osoan eta ikuspegi hobea lortuko duzu"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Eremu pribatua"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Partekatua"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Eremu pribatua"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Jakinarazpenaren kontuzko edukia ezkutatu da"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Aplikazioko edukia ezkutatu egin da pantaila partekatzeko eginbidetik, segurtasuna bermatzeko"</string>
diff --git a/core/res/res/values-fa/donottranslate-cldr.xml b/core/res/res/values-fa/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 308260f..d761c8a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"پین بیش از حد کوتاه است. باید حداقل ۴ رقم باشد."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"بعداً دوباره امتحان کنید"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"مشاهده در حالت تمام صفحه"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"برای خروج، انگشتتان را از بالای صفحه به پایین بکشید."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"متوجه شدم"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"برای دید بهتر، دستگاه را بچرخانید"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"برای دید بهتر، <xliff:g id="NAME">%s</xliff:g> را به‌صورت تمام‌صفحه باز کنید"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"فضای خصوصی"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"همسانه‌سازی"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"همگانی"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"فضای خصوصی"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"محتوای اعلان حساس پنهان شده است"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"به‌دلایل امنیتی، محتوای برنامه از دید هم‌رسانی صفحه‌نمایش پنهان شد"</string>
diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-fi/donottranslate-cldr.xml b/core/res/res/values-fi/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index db579f2..9a7bfff 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-koodi on liian lyhyt. Vähimmäispituus on neljä merkkiä."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Yritä myöhemmin uudelleen"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Koko ruudun tilassa"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Sulje palkki pyyhkäisemällä alas ruudun ylälaidasta."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Selvä"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Kierrä, niin saat paremman näkymän"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Avaa <xliff:g id="NAME">%s</xliff:g>, niin saat paremman näkymän"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Yksityinen tila"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klooni"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Yhteinen"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Yksityinen tila"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Arkaluontoisen ilmoituksen sisältö piilotettu"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sovelluksen sisältö piilotettu näytön jakamiselta turvallisuussyistä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index a2443d7..cc56526 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -717,7 +717,7 @@
     <string name="face_acquired_too_right" msgid="6245286514593540859">"Déplacez le téléphone vers la gauche"</string>
     <string name="face_acquired_too_left" msgid="9201762240918405486">"Déplacez le téléphone vers la droite"</string>
     <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Veuillez regarder plus directement votre appareil."</string>
-    <string name="face_acquired_not_detected" msgid="1057966913397548150">"Visage non détecté. Tenez votre téléphone à hauteur des yeux."</string>
+    <string name="face_acquired_not_detected" msgid="1057966913397548150">"Visage non détecté. Tenez le téléphone au niveau des yeux."</string>
     <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Trop de mouvement. Tenez le téléphone immobile."</string>
     <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Veuillez inscrire votre visage à nouveau."</string>
     <string name="face_acquired_too_different" msgid="4505278456634706967">"Visage non reconnu. Réessayez."</string>
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Le NIP est trop court. Il doit comporter au moins 4 chiffres."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Réessayez plus tard"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Affichage plein écran"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Pour quitter, balayez vers le bas à partir du haut."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Faire pivoter pour obtenir un meilleur affichage"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Ouvrir <xliff:g id="NAME">%s</xliff:g> en plein écran pour un meilleur affichage"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu confidentiel de la notification est masqué"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'application est masqué du Partage d\'écran par mesure de sécurité"</string>
diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c69af7c..f1a81a3 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Le code PIN est trop court. Il doit comporter au moins 4 chiffres."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Veuillez réessayer ultérieurement."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Affichage en plein écran"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Pour quitter, balayez l\'écran du haut vers le bas."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Faites pivoter pour mieux voir"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Ouvrez <xliff:g id="NAME">%s</xliff:g> en plein écran pour mieux voir"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu sensible de la notification a été masqué"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'appli est masqué lors du partage d\'écran pour des raisons de sécurité"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 0ffd299..c29d888 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"O PIN é demasiado curto. Debe conter polo menos 4 díxitos."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Téntao de novo máis tarde"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vendo pantalla completa"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Para saír, pasa o dedo cara abaixo desde a parte superior."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendido"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Xira a pantalla para que se vexa mellor"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abre <xliff:g id="NAME">%s</xliff:g> en pantalla completa para unha mellor visualización"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espazo privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonado"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espazo privado"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Contido confidencial da notificación oculto"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Por motivos de seguranza, ocultouse o contido da aplicación para que no se mostre na pantalla compartida"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 55032c9..8fe9a5b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1433,7 +1433,7 @@
     <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"ભાષા અને લેઆઉટ પસંદ કરવા માટે ટૅપ કરો"</string>
     <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-    <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"અન્ય ઍપથી ઉપર બતાવો"</string>
+    <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"અન્ય ઍપની ઉપર ડિસ્પ્લે કરો"</string>
     <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> અન્ય ઍપ્લિકેશનોની ઉપર પ્રદર્શિત થઈ રહ્યું છે"</string>
     <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> અન્ય ઍપ્લિકેશનો પર દેખાઈ છે"</string>
     <string name="alert_windows_notification_message" msgid="6538171456970725333">"જો તમે નથી ઇચ્છતા કે <xliff:g id="NAME">%s</xliff:g> આ સુવિધાનો ઉપયોગ કરે, તો સેટિંગ ખોલવા માટે ટૅપ કરો અને તેને બંધ કરો."</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"પિન ખૂબ નાનો છે. ઓછામાં ઓછો 4 અંકનો હોવો આવશ્યક છે."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"પછી ફરી પ્રયાસ કરો"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"પૂર્ણ સ્ક્રીન પર જુઓ"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"બહાર નીકળવા માટે, ટોચ પરથી નીચે સ્વાઇપ કરો."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"સમજાઈ ગયું"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"બહેતર વ્યૂ માટે ફેરવો"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"બહેતર વ્યૂ માટે, પૂર્ણ સ્ક્રીનમાં <xliff:g id="NAME">%s</xliff:g> ખોલો"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ખાનગી સ્પેસ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ક્લોન"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"કૉમ્યુનલ"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ખાનગી સ્પેસ"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"સંવેદનશીલ માહિતીવાળા નોટિફિકેશનનું કન્ટેન્ટ છુપાવ્યું"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"સુરક્ષા માટે સ્ક્રીન શેર કરતી વખતે ઍપનું કન્ટેન્ટ છુપાવેલું છે"</string>
diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hi/donottranslate-cldr.xml b/core/res/res/values-hi/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index e47715a..39e6625 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN बहुत छोटा है. कम से कम 4 अंकों का होना चाहिए."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"बाद में फिर से प्रयास करें"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"आप पूरे स्क्रीन पर देख रहे हैं"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"बाहर निकलने के लिए, ऊपर से नीचे स्वा‍इप करें."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ठीक है"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"बेहतर व्यू पाने के लिए, डिवाइस की स्क्रीन को घुमाएं"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"बेहतर व्यू पाने के लिए <xliff:g id="NAME">%s</xliff:g> को फ़ुल स्क्रीन में खोलें"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"प्राइवेट स्पेस"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"कम्यूनिटी"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"प्राइवेट स्पेस"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील जानकारी वाली सूचना का कॉन्टेंट छिपा है"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेयर करने के दौरान सुरक्षा के लिए, ऐप्लिकेशन का कॉन्टेंट छिपाया गया"</string>
diff --git a/core/res/res/values-hr-rHR/donottranslate-cldr.xml b/core/res/res/values-hr-rHR/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hr/donottranslate-cldr.xml b/core/res/res/values-hr/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0b9b662..08ea4a2 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1413,7 +1413,7 @@
     <string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Omogućen je način testnog okvira"</string>
     <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Vratite na tvorničke postavke da biste onemogućili način testnog okvira."</string>
     <string name="console_running_notification_title" msgid="6087888939261635904">"Serijska konzola omogućena"</string>
-    <string name="console_running_notification_message" msgid="7892751888125174039">"Izvedba je otežana. Provjerite početni program za pokretanje da biste onemogućili konzolu."</string>
+    <string name="console_running_notification_message" msgid="7892751888125174039">"Izvedba je otežana. Provjerite pokretač operativnog sustava da biste onemogućili konzolu."</string>
     <string name="mte_override_notification_title" msgid="4731115381962792944">"Omogućen je eksperimentalni MTE"</string>
     <string name="mte_override_notification_message" msgid="2441170442725738942">"To može utjecati na izvedbu i stabilnost. Ponovno pokrenite da biste onemogućili. Ako je omogućeno pomoću arm64.memtag.bootctl, prethodno postavite na \"none\"."</string>
     <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"Tekućina ili prljavština u USB priključku"</string>
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN je prekratak. Mora imati barem 4 znamenke."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Pokušajte ponovo kasnije"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Gledanje preko cijelog zaslona"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Za izlaz prijeđite prstom od vrha prema dolje."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Shvaćam"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zakrenite kako biste bolje vidjeli"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otvorite aplikaciju <xliff:g id="NAME">%s</xliff:g> preko cijelog zaslona za bolji prikaz"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Skriven je osjetljiv sadržaj obavijesti"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije sakriven je od dijeljenja zaslona radi sigurnosti"</string>
diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hu/donottranslate-cldr.xml b/core/res/res/values-hu/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index de6777e..729fad9 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"A PIN-kód túl rövid. Legalább 4 számjegyből kell állnia."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Próbálkozzon később"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Megtekintése teljes képernyőn"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Kilépéshez csúsztassa ujját fentről lefelé."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Értem"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Forgassa el a jobb élmény érdekében"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"A jobb élmény érdekében teljes képernyős módban nyissa meg a(z) <xliff:g id="NAME">%s</xliff:g> alkalmazást"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privát terület"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klón"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Közös"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privát terület"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Bizalmas értesítéstartalom elrejtve"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"A biztonság érdekében a képernyőmegosztástól elrejtett alkalmazástartalom"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 58331c7..c3eec0f 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-ը չափազանց կարճ է: Պետք է ունենա առնվազն 4 թվանիշ:"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Կրկին փորձեք մի փոքր ուշ"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Լիաէկրան դիտում"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Դուրս գալու համար վերևից սահահարվածեք դեպի ներքև:"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Պարզ է"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Պտտեք՝ դիտակերպը լավացնելու համար"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Բացեք <xliff:g id="NAME">%s</xliff:g> հավելվածը լիաէկրան ռեժիմում՝ դիտակերպը լավացնելու համար"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Մասնավոր տարածք"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Կլոն"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ընդհանուր"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Մասնավոր տարածք"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Ծանուցման զգայուն բովանդակությունը թաքցված է"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Անվտանգության նկատառումներից ելնելով՝ հավելվածի բովանդակությունը թաքցվել է էկրանի ցուցադրումից"</string>
diff --git a/core/res/res/values-in-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-in/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index eeaf2b6..34a52e4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN terlalu pendek. Minimal 4 digit."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Coba lagi nanti"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Melihat layar penuh"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Untuk keluar, geser layar ke bawah dari atas."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Mengerti"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Putar posisi layar untuk mendapatkan tampilan yang lebih baik"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Buka <xliff:g id="NAME">%s</xliff:g> dalam layar penuh untuk mendapatkan tampilan yang lebih baik"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Konten notifikasi sensitif disembunyikan"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Konten aplikasi disembunyikan dari berbagi layar untuk alasan keamanan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 7e68a06..9495626 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-númerið er of stutt. Það verður að vera a.m.k. 4 tölustafir."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Reyndu aftur síðar"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Notar allan skjáinn"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Strjúktu niður frá efri brún til að hætta."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ég skil"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Snúðu til að sjá betur"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Opnaðu <xliff:g id="NAME">%s</xliff:g> á öllum skjánum til að fá betra yfirlit"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Leynirými"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Afrit"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Sameiginlegt"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Leynirými"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Viðkvæmt tilkynningaefni falið"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Efni forrits falið í skjádeilingu af öryggisástæðum"</string>
diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 5ed3706..3952837 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Il PIN è troppo corto. Deve avere almeno quattro cifre."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Riprova più tardi"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualizzazione a schermo intero"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Per uscire, scorri dall\'alto verso il basso."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ruota per migliorare l\'anteprima"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Apri <xliff:g id="NAME">%s</xliff:g> a schermo intero per migliorare la visualizzazione"</string>
@@ -2037,7 +2038,7 @@
     <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Tocca per visualizzare i file"</string>
     <string name="pin_target" msgid="8036028973110156895">"Fissa"</string>
     <string name="pin_specific_target" msgid="7824671240625957415">"Blocca <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="unpin_target" msgid="3963318576590204447">"Sgancia"</string>
+    <string name="unpin_target" msgid="3963318576590204447">"Sblocca"</string>
     <string name="unpin_specific_target" msgid="3859828252160908146">"Sblocca <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="app_info" msgid="6113278084877079851">"Informazioni app"</string>
     <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spazio privato"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Condiviso"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spazio privato"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Contenuti sensibili della notifica nascosti"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenuti dell\'app nascosti dalla condivisione schermo per sicurezza"</string>
diff --git a/core/res/res/values-iw/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a1e72da..9c143c7 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"קוד הגישה קצר מדי. חייב להיות באורך 4 ספרות לפחות."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"יש לנסות שוב מאוחר יותר"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"צפייה במסך מלא"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"כדי לצאת, פשוט מחליקים אצבע מלמעלה למטה."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"הבנתי"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"מסובבים כדי לראות טוב יותר"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"צריך לפתוח את <xliff:g id="NAME">%s</xliff:g> במסך מלא כדי לראות טוב יותר"</string>
@@ -2039,7 +2040,7 @@
     <string name="pin_specific_target" msgid="7824671240625957415">"הצמדה של‏ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="unpin_target" msgid="3963318576590204447">"ביטול הצמדה"</string>
     <string name="unpin_specific_target" msgid="3859828252160908146">"ביטול ההצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="app_info" msgid="6113278084877079851">"פרטי אפליקציה"</string>
+    <string name="app_info" msgid="6113278084877079851">"פרטי האפליקציה"</string>
     <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"תהליך ההדגמה מתחיל…"</string>
     <string name="demo_restarting_message" msgid="1160053183701746766">"מתבצע איפוס של המכשיר…"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"המרחב הפרטי"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"שכפול"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"שיתופי"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"המרחב הפרטי"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"יש תוכן רגיש בהתראה שהוסתר"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"תוכן האפליקציה מוסתר משיתוף המסך מטעמי אבטחה"</string>
diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 04e47a7..934967f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -716,7 +716,7 @@
     <string name="face_acquired_too_right" msgid="6245286514593540859">"スマートフォンを左に動かしてください"</string>
     <string name="face_acquired_too_left" msgid="9201762240918405486">"スマートフォンを右に動かしてください"</string>
     <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"もっとまっすぐデバイスに顔を向けてください。"</string>
-    <string name="face_acquired_not_detected" msgid="1057966913397548150">"顔を確認できません。スマートフォンを目の高さに合わせます。"</string>
+    <string name="face_acquired_not_detected" msgid="1057966913397548150">"顔を確認できません。目の高さに合わせてください"</string>
     <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"あまり動かさないでください。安定させてください。"</string>
     <string name="face_acquired_recalibrate" msgid="8724013080976469746">"顔を登録し直してください。"</string>
     <string name="face_acquired_too_different" msgid="4505278456634706967">"顔を認識できません。もう一度お試しください。"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PINが短すぎます。4桁以上で設定してください。"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"しばらくしてから再試行"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"全画面表示"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"終了するには、上から下にスワイプします。"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"画面を回転させて見やすくしましょう"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"<xliff:g id="NAME">%s</xliff:g> を全画面表示で開いて見やすくしましょう"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"プライベート スペース"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"プライベート スペース"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"プライベートな通知内容は表示されません"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"セキュリティ上、画面共有ではアプリの内容は非表示となります"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ef37d4f..013125e 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN ძალიან მოკლეა. უნდა შედგებოდეს სულ ცოტა 4 ციფრისგან."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"სცადეთ მოგვიანებით"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"სრულ ეკრანზე ნახვა"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"გამოსვლისათვის, გაასრიალეთ ზემოდან ქვემოთ."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"გასაგებია"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"შეატრიალეთ უკეთესი ხედისთვის"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"გახსენით <xliff:g id="NAME">%s</xliff:g> სრულ ეკრანზე უკეთესი ხედისთვის"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"კერძო სივრცე"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"კლონის შექმნა"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"საერთო"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"კერძო სივრცე"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"სენსიტიური შეტყობინების კონტენტი დამალულია"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ეკრანის გაზიარებიდან აპის კონტენტი დამალულია უსაფრთხოების მიზნით"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index f36d9fc..791644d 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN тым қысқа. Кем дегенде 4 бірлік болуы тиіс."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Кейінірек қайта әрекеттеніңіз."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Толық экранда көру"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Шығу үшін жоғарыдан төмен қарай сырғытыңыз."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Түсінікті"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Жақсырақ көру үшін бұрыңыз."</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Жақсырақ көру үшін <xliff:g id="NAME">%s</xliff:g> қолданбасын толық экранда ашыңыз."</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Құпия кеңістік"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Құпия кеңістік"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Хабарландырудың құпия контенті жасырылған."</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Қауіпсіздік мақсатында қолданба контенті экранды көрсету кезінде жасырылды."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 195eb13..12e1a36 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"កូដ​ PIN ខ្លី​ពេក។ ត្រូវ​តែ​មាន​យ៉ាង​ហោច​ណាស់ ៤ តួ។"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"សូម​ព្យាយាម​ម្ដងទៀត​នៅ​ពេល​ក្រោយ។"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"កំពុងមើលពេញអេក្រង់"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"ដើម្បីចាកចេញ សូមអូសពីលើចុះក្រោម។"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"យល់ហើយ"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"បង្វិលដើម្បីមើលបានកាន់តែច្បាស់"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"បើក <xliff:g id="NAME">%s</xliff:g> នៅក្នុងអេក្រង់ពេញ ដើម្បីមើលបានកាន់តែច្បាស់"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"លំហ​ឯកជន"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ក្លូន"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ទូទៅ"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"លំហ​ឯកជន"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"បានលាក់ខ្លឹមសារជូនដំណឹងដែលមានលក្ខណៈរសើប"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"បានលាក់ខ្លឹមសារកម្មវិធីពីការបង្ហាញ​អេក្រង់ដើម្បីសុវត្ថិភាព"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 073e448..c134b74 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"ಪಿನ್‌ ತುಂಬಾ ಚಿಕ್ಕದಾಗಿದೆ. ಕನಿಷ್ಠ ಪಕ್ಷ 4 ಅಂಕಿಗಳಾಗಿರಬೇಕು."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"ಪೂರ್ಣ ಪರದೆಯನ್ನು ವೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"ನಿರ್ಗಮಿಸಲು, ಮೇಲಿನಿಂದ ಕೆಳಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ತಿಳಿಯಿತು"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ಅತ್ಯುತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ತಿರುಗಿಸಿ"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ಅತ್ಯುತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ <xliff:g id="NAME">%s</xliff:g> ಅನ್ನು ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ಕ್ಲೋನ್"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ಸಮುದಾಯ"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"ಸೂಕ್ಷ್ಮ ನೋಟಿಫಿಕೇಶನ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ಭದ್ರತೆಗಾಗಿ ಸ್ಕ್ರೀನ್‌‌ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯಲ್ಲಿ ಆ್ಯಪ್ ಕಂಟೆಂಟ್‌ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index bbe8aef..62ec778 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN이 너무 짧습니다. 최소 4자 이상이어야 합니다."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"나중에 다시 시도"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"전체 화면 모드"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"종료하려면 위에서 아래로 스와이프합니다."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"확인"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"카메라 미리보기 화면이 잘 보이도록 회전하세요."</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"화면이 더 잘 보이도록 <xliff:g id="NAME">%s</xliff:g>을(를) 전체 화면에서 여세요."</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"비공개 스페이스"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"클론"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"공동"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"비공개 스페이스"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"민감한 알림 콘텐츠 숨김"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"보안을 위해 화면 공유에서 앱 콘텐츠가 숨겨집니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 9686a14..4df1e7e 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN өтө кыска. Аз дегенде 4 сандан турушу керек."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Бир аздан кийин кайталап көрүңүз"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Толук экран режими"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Чыгуу үчүн экранды ылдый сүрүп коюңуз."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Түшүндүм"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Жакшыраак көрүү үчүн буруңуз"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Жакшыраак көрүү үчүн <xliff:g id="NAME">%s</xliff:g> колдонмосун толук экранда ачыңыз"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Жеке мейкиндик"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Жеке мейкиндик"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Купуя билдирменин мазмуну жашырылган"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Коопсуздук үчүн колдонмодогу контент бөлүшүлгөн экрандан жашырылды"</string>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index f58c4b0..c0916bc 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -69,7 +69,7 @@
     <dimen name="timepicker_left_side_width">250dip</dimen>
 
     <!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
-    <dimen name="immersive_mode_cling_width">380dp</dimen>
+    <dimen name="immersive_mode_cling_width">500dp</dimen>
 
      <!-- Floating toolbar dimensions -->
      <dimen name="floating_toolbar_preferred_width">544dp</dimen>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index b6bd4b2..3b77acb 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN ​ສັ້ນ​ເກີນ​ໄປ​. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ​."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"ລອງໃໝ່ອີກຄັ້ງໃນພາຍຫລັງ."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"ການ​ເບິ່ງ​ເຕັມ​ໜ້າ​ຈໍ"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"ຫາກຕ້ອງການອອກ, ໃຫ້ຮູດຈາກທາງເທິງລົງມາທາງລຸ່ມ."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ເຂົ້າ​ໃຈ​ແລ້ວ"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ໝຸນເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ເປີດ <xliff:g id="NAME">%s</xliff:g> ໃນໂໝດເຕັມຈໍເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ໂຄລນ"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ສ່ວນກາງ"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"ເນື້ອຫາການແຈ້ງເຕືອນທີ່ລະອຽດອ່ອນເຊື່ອງຢູ່"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ເນື້ອຫາແອັບຖືກເຊື່ອງໄວ້ຈາກການແບ່ງປັນໜ້າຈໍເພື່ອຄວາມປອດໄພ"</string>
diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-lt/donottranslate-cldr.xml b/core/res/res/values-lt/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 2b4f370..b864773 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1885,7 +1885,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN kodas per trumpas. Jis turi būti bent 4 skaitmenų."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Vėliau bandykite dar kartą"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Peržiūrima viso ekrano režimu"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Jei norite išeiti, perbraukite žemyn iš viršaus."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Supratau"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Pasukite, kad geriau matytumėte vaizdą"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Atidarykite „<xliff:g id="NAME">%s</xliff:g>“ viso ekrano režimu, kad geriau matytumėte vaizdą"</string>
@@ -2409,6 +2410,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privati erdvė"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonuoti"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Bendruomenės"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privati erdvė"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Neskelbtinos informacijos pranešimo turinys paslėptas"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Programos turinys paslėptas bendrinant ekraną saugumo sumetimais"</string>
diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-lv/donottranslate-cldr.xml b/core/res/res/values-lv/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 3669f04..9147e9b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN ir pārāk īss. Tam ir jābūt vismaz 4 ciparus garam."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Vēlāk mēģiniet vēlreiz."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Skatīšanās pilnekrāna režīmā"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Lai izietu, no augšdaļas velciet lejup."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Labi"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Lai uzlabotu skatu, pagrieziet ekrānu."</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Labākam skatam atveriet lietotni <xliff:g id="NAME">%s</xliff:g> pilnekrāna režīmā."</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privātā telpa"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klons"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kopīgs"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privātā telpa"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitīvs paziņojuma saturs ir paslēpts"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Drošības nolūkos lietotnes saturs kopīgotajā ekrānā ir paslēpts"</string>
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 8902619..d305c87 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -209,7 +209,7 @@
     <string name="location_changed_notification_text" msgid="7158423339982706912">"Контактирајте со IT-администраторот за да дознаете повеќе"</string>
     <string name="geofencing_service" msgid="3826902410740315456">"Услуга за виртуелна географска граница"</string>
     <string name="country_detector" msgid="7023275114706088854">"Детектор на земја"</string>
-    <string name="location_service" msgid="2439187616018455546">"Услуга за локација"</string>
+    <string name="location_service" msgid="2439187616018455546">"Локациска услуга"</string>
     <string name="gnss_service" msgid="8907781262179951385">"Услуга GNSS"</string>
     <string name="sensor_notification_service" msgid="7474531979178682676">"Услуга за известување од сензорот"</string>
     <string name="twilight_service" msgid="8964898045693187224">"Услуга за самрак"</string>
@@ -498,9 +498,9 @@
     <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"пристапи кон наредби на давателот на дополнителна локација"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Овозможува апликацијата да пристапи кон дополнителни наредби на давател на локација. Ова може да овозможи апликацијата да го попечи функционирањето на GPS или други извори на локација."</string>
     <string name="permlab_accessFineLocation" msgid="6426318438195622966">"пристап до прецизната локација само во преден план"</string>
-    <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Апликацијава може да ја добие вашата прецизна локација од „Услугите според локација“ кога се користи. „Услугите според локација“ за уредот мора да се вклучени ако сакате апликацијата да ја добие локацијата. Ова може да го зголеми користењето на батеријата."</string>
+    <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Апликацијава може да ја добие вашата прецизна локација од „Локациските услуги“ кога се користи. „Локациските услуги“ за уредот мора да се вклучени ако сакате апликацијата да ја добие локацијата. Ова може да го зголеми користењето на батеријата."</string>
     <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"пристап до приближната локација само во преден план"</string>
-    <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Апликацијава може да ја добие вашата приближна локација од „Услугите според локација“ кога се користи. „Услугите според локација“ за уредот мора да се вклучени ако сакате апликацијата да ја добие локацијата."</string>
+    <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Апликацијава може да ја добие вашата приближна локација од „Локациските услуги“ кога се користи. „Локациските услуги“ за уредот мора да се вклучени ако сакате апликацијата да ја добие локацијата."</string>
     <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"пристап до локацијата во заднина"</string>
     <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Апликацијава може да пристапува до локацијата во секое време, дури и кога не се користи."</string>
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"менува аудио поставки"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN кодот е премногу краток. Мора да има најмалку 4 цифри."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Обиди се повторно подоцна"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Се прикажува на цел екран"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"За да излезете, повлечете одозгора надолу."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Сфатив"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ротирајте за подобар приказ"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"За подобар приказ, отворете ја апликацијата <xliff:g id="NAME">%s</xliff:g> на цел екран"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватен простор"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониран профил"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Профил на заедницата"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватен простор"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Содржината на чувствителните известувања е скриена"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Од безбедносни причини, содржините на апликацијата се скриени од споделувањето екран"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 005d7cb..ede15a1 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"പിൻ തീരെ ചെറുതാണ്. 4 അക്കമെങ്കിലും ഉണ്ടായിരിക്കണം."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"പിന്നീട് വീണ്ടും ശ്രമിക്കുക"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"പൂർണ്ണ സ്‌ക്രീനിൽ കാണുന്നു"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"അവസാനിപ്പിക്കാൻ, മുകളിൽ നിന്ന് താഴോട്ട് സ്വൈപ്പ് ചെയ്യുക."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"മനസ്സിലായി"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"മികച്ച കാഴ്‌ചയ്‌ക്കായി റൊട്ടേറ്റ് ചെയ്യുക"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"മികച്ച കാഴ്‌‌ചയ്ക്ക് പൂർണ്ണ സ്‌ക്രീനിൽ <xliff:g id="NAME">%s</xliff:g> തുറക്കുക"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"സ്വകാര്യ സ്പേസ്"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ക്ലോൺ ചെയ്യുക"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"കമ്മ്യൂണൽ"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"സ്വകാര്യ സ്പേസ്"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"സൂക്ഷ്‌മമായി കൈകാര്യം ചെയ്യേണ്ട അറിയിപ്പ് ഉള്ളടക്കം മറച്ചിരിക്കുന്നു"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ആപ്പ് ഉള്ളടക്കം, അതിന്റെ സുരക്ഷയ്ക്കായി സ്ക്രീൻ പങ്കിടലിൽ നിന്ന് മറച്ചിരിക്കുന്നു"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index f7bf6ca..955b025 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"ПИН хэт богино байна. Хамгийн багадаа 4 цифртэй байх ёстой."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Дараа дахин оролдоно уу"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Бүтэн дэлгэцээр үзэж байна"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Гарахаар бол дээрээс нь доош нь чирнэ үү."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ойлголоо"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Харагдах байдлыг сайжруулах бол эргүүлнэ үү"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Харагдах байдлыг сайжруулах бол <xliff:g id="NAME">%s</xliff:g>-г бүтэн дэлгэцээр нээнэ үү"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Хаалттай орон зай"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Нийтийн"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Хаалттай орон зай"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Эмзэг мэдэгдлийн контентыг нуусан"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Аюулгүй байдлын улмаас аппын контентыг дэлгэц хуваалцахаас нуусан"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 77a58af..2df1bc9 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"पिन खूप लहान आहे. किमान 4 अंकांचा असणे आवश्‍यक आहे."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"नंतर पुन्हा प्रयत्न करा"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"पूर्ण स्क्रीनवर पाहत आहात"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"बाहेर पडण्यासाठी, वरून खाली स्वाइप करा."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"समजले"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"अधिक चांगल्या दृश्यासाठी फिरवा"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"अधिक चांगल्या दृश्यासाठी <xliff:g id="NAME">%s</xliff:g> हे फुल स्क्रीनमध्ये उघडा"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"खाजगी स्पेस"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"खाजगी स्पेस"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील नोटिफिकेशनचा आशय लपवलेला आहे"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेअर करताना सुरक्षेसाठी अ‍ॅपमधील आशय लपवला आहे"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 96691f6..31180be 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN terlalu pendek. Mesti sekurang-kurangnya 4 angka."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Cuba sebentar lagi"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Melihat skrin penuh"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Untuk keluar, leret dari atas ke bawah."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Faham"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Putar untuk mendapatkan paparan yang lebih baik"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Buka <xliff:g id="NAME">%s</xliff:g> dalam skrin penuh untuk mendapatkan paparan yang lebih baik"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Kandungan pemberitahuan yang sensitif disembunyikan"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Kandungan apl disembunyikan daripada perkongsian skrin untuk keselamatan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index c1b7822..f1ab9e0 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"ပင် နံပါတ် တိုလွန်းသည်။. အနည်းဆုံး ဂဏန်း ၄ လုံး ဖြစ်ရမည်။"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"နောက်မှ ပြန်ကြိုးစားပါ"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"မျက်နှာပြင်အပြည့် ကြည့်နေသည်"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"ထွက်ရန် အပေါ်မှ အောက်သို့ ဆွဲချပါ။"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ရပါပြီ"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ပိုကောင်းသောမြင်ကွင်းအတွက် လှည့်ပါ"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဖန်သားပြင်အပြည့်ဖြင့် <xliff:g id="NAME">%s</xliff:g> ကို ဖွင့်ပါ"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"သီးသန့်နေရာ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ပုံတူပွားရန်"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"အများသုံး"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"သီးသန့်နေရာ"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"သတိထားရမည့် အကြောင်းကြားချက်ပါ အချက်အလက်ကို ဖျောက်ထားသည်"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"အက်ပ်အကြောင်းအရာသည် လုံခြုံရေးအတွက် မျက်နှာပြင် မျှဝေခြင်းမှ ဖျောက်ထားသည်"</string>
diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 23f675e..456366f 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -342,7 +342,7 @@
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"Samtalelogger"</string>
     <string name="permgroupdesc_calllog" msgid="2026996642917801803">"lese og skrive samtaleloggen"</string>
     <string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string>
-    <string name="permgroupdesc_phone" msgid="270048070781478204">"ring og administrer anrop"</string>
+    <string name="permgroupdesc_phone" msgid="270048070781478204">"ringe og administrere anrop"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Kroppssensorer"</string>
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"få tilgang til sensordata om de vitale tegnene dine"</string>
     <string name="permgrouplab_notifications" msgid="5472972361980668884">"Varsler"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-koden er for kort. Den må bestå av minst fire sifre."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Prøv på nytt senere"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visning i fullskjerm"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Sveip ned fra toppen for å avslutte."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Skjønner"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Roter for å få en bedre visning"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Åpne <xliff:g id="NAME">%s</xliff:g> i fullskjerm for å se bedre"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Felles"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitivt varselinnhold er skjult"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av sikkerhetsgrunner er appinnholdet skjult for skjermdelingen"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index d25a7a3..55c2ef4 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN अति छोटो भयो। कम्तीमा ४ अङ्क हुन आवश्यक छ।"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"पछि पुनः प्रयास गर्नुहोस्"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"पूरा पर्दा हेर्दै"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"बाहिर निस्कन, माथिबाट तल स्वाइप गर्नुहोस्।"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"बुझेँ"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"अझ राम्रो दृश्य हेर्न चाहनुहुन्छ भने रोटेट गर्नुहोस्"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"अझ राम्रो दृश्य हेर्न चाहनुहुन्छ भने <xliff:g id="NAME">%s</xliff:g> खोल्नुहोस्"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"निजी स्पेस"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"निजी स्पेस"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील सूचनासम्बन्धी सामग्री लुकाइएको छ"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रिन सेयर गर्दा सुरक्षाका लागि एपमा भएको सामग्री लुकाइएको छ"</string>
diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 0b2c507..b1b3060 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Pincode is te kort. Moet ten minste vier cijfers lang zijn."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Probeer het later opnieuw"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Volledig scherm wordt getoond"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ik snap het"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Draai voor een betere weergave"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Open <xliff:g id="NAME">%s</xliff:g> op volledig scherm voor een betere weergave"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privégedeelte"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenschappelijk"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privégedeelte"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Content van gevoelige meldingen verborgen"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-content verborgen voor scherm delen vanwege beveiligingsrisico\'s"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index f3cd70e..b56c114 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1751,7 +1751,7 @@
     <string name="color_correction_feature_name" msgid="7975133554160979214">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ଏକ-ହାତ ମୋଡ୍"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ଅତ୍ୟଧିକ ଡିମ"</string>
-    <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string>
+    <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ଭଲ୍ୟୁମ କୀ\'ଗୁଡ଼ିକୁ ରିଲିଜ କରନ୍ତୁ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g>କୁ ଚାଲୁ କରିବା ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ କୀ\'କୁ ପୁଣି 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇ ଧରି ରଖନ୍ତୁ।"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN ବହୁତ ଛୋଟ। ଅତି କମ୍‍ରେ 4 ସଂଖ୍ୟା ବିଶିଷ୍ଟ ହେବା ଦରକାର।"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ଦେଖାଯାଉଛି"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"ବାହାରିବା ପାଇଁ, ଉପରୁ ତଳକୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ।"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ବୁଝିଗଲି"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ଏକ ଭଲ ଭ୍ୟୁ ପାଇଁ ରୋଟେଟ କରନ୍ତୁ"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ଏକ ଭଲ ଭ୍ୟୁ ପାଇଁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନରେ <xliff:g id="NAME">%s</xliff:g> ଖୋଲନ୍ତୁ"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"କ୍ଲୋନ"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"କମ୍ୟୁନାଲ"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"ସମ୍ୱେଦନଶୀଳ ବିଜ୍ଞପ୍ତି ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ସୁରକ୍ଷା ପାଇଁ ସ୍କ୍ରିନ ସେୟାରରୁ ଆପ ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 60d8737..72598d1 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"ਪਿੰਨ ਬਹੁਤ ਜ਼ਿਆਦਾ ਛੋਟਾ ਹੈ। ਘੱਟੋ-ਘੱਟ 4 ਅੰਕ ਹੋਣੇ ਚਾਹੀਦੇ ਹਨ।"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦੇਖੋ"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"ਬਾਹਰ ਜਾਣ ਲਈ, ਉਪਰੋਂ ਹੇਠਾਂ ਸਵਾਈਪ ਕਰੋ।"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ਸਮਝ ਲਿਆ"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਅਨੁਭਵ ਲਈ ਘੁਮਾਓ"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਅਨੁਭਵ ਲਈ <xliff:g id="NAME">%s</xliff:g> ਨੂੰ ਪੂਰੀ-ਸਕ੍ਰੀਨ ਵਿੱਚ ਖੋਲ੍ਹੋ"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ਕਲੋਨ"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ਭਾਈਚਾਰਕ"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"ਲੁਕੀ ਹੋਈ ਸੰਵੇਦਨਸ਼ੀਲ ਸੂਚਨਾ ਸਮੱਗਰੀ"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ਐਪ ਸਮੱਗਰੀ ਨੂੰ ਸੁਰੱਖਿਆ ਲਈ ਸਕ੍ਰੀਨ ਸਾਂਝਾਕਰਨ ਤੋਂ ਲੁਕਾਇਆ ਗਿਆ ਹੈ"</string>
diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ec5fee7..f19b0d0 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1885,7 +1885,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN jest za krótki. Musi mieć co najmniej 4 cyfry."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Spróbuj ponownie później"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Włączony pełny ekran"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Aby wyjść, przesuń palcem z góry na dół."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Obróć, aby lepiej widzieć"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otwórz aplikację <xliff:g id="NAME">%s</xliff:g> na pełnym ekranie, aby lepiej widzieć"</string>
@@ -2409,6 +2410,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Przestrzeń prywatna"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wspólny"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Przestrzeń prywatna"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Treść poufnego powiadomienia została ukryta"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ze względów bezpieczeństwa zawartość aplikacji jest niewidoczna podczas udostępniania ekranu"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 807c4ce..6bc9fe0 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1380,7 +1380,7 @@
     <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar serviço móvel"</string>
     <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Baixe o app da operadora para ativar seu novo chip"</string>
     <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Baixe o app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
-    <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Fazer download do app"</string>
+    <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Baixar o app"</string>
     <string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo chip inserido"</string>
     <string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
     <string name="time_picker_dialog_title" msgid="9053376764985220821">"Definir hora"</string>
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"O PIN é curto demais. Deve ter pelo menos 4 dígitos."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Tente novamente mais tarde"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualização em tela cheia"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize de cima para baixo."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendi"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gire a tela para ter uma visualização melhor"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abra o app <xliff:g id="NAME">%s</xliff:g> em tela cheia para ter uma melhor visualização"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string>
diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 0dcf4c5..5cf49fbd 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -733,7 +733,7 @@
     <skip />
     <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Não é possível criar o seu modelo de rosto. Tente novamente."</string>
     <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detetados. O seu rosto tem de estar completamente visível."</string>
-    <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detetada. Todo o rosto tem de estar visível"</string>
+    <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Cobertura facial detetada. O seu rosto tem de estar completamente visível."</string>
   <string-array name="face_acquired_vendor">
   </string-array>
     <string name="face_error_hw_not_available" msgid="5085202213036026288">"Não pode validar o rosto. Hardware não disponível."</string>
@@ -1292,7 +1292,7 @@
     <string name="android_upgrading_complete" msgid="409800058018374746">"A concluir o arranque."</string>
     <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente ao configurar a sua impressão digital."</string>
     <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Para terminar, desligue o ecrã"</string>
-    <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desligar"</string>
+    <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desativar"</string>
     <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a validar a impressão digital?"</string>
     <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente para validar a sua impressão digital."</string>
     <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar ecrã"</string>
@@ -1438,7 +1438,7 @@
     <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"A app <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras aplicações"</string>
     <string name="alert_windows_notification_title" msgid="6331662751095228536">"O <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras app"</string>
     <string name="alert_windows_notification_message" msgid="6538171456970725333">"Se não quer que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string>
-    <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desligar"</string>
+    <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desativar"</string>
     <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"A verificar o <xliff:g id="NAME">%s</xliff:g>…"</string>
     <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"A rever o conteúdo atual…"</string>
     <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"A analisar o armazenamento de multimédia"</string>
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"O PIN é demasiado pequeno. Deve ter, no mínimo, 4 dígitos."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Tente mais tarde"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualização de ecrã inteiro"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize rapidamente para baixo a partir da parte superior."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rode para uma melhor visualização"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abra <xliff:g id="NAME">%s</xliff:g> em ecrã inteiro para uma melhor visualização"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comum"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo das notificações sensíveis ocultado"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo da app ocultado da partilha de ecrã por motivos de segurança"</string>
diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 807c4ce..6bc9fe0 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1380,7 +1380,7 @@
     <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Ativar serviço móvel"</string>
     <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Baixe o app da operadora para ativar seu novo chip"</string>
     <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Baixe o app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
-    <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Fazer download do app"</string>
+    <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Baixar o app"</string>
     <string name="carrier_app_notification_title" msgid="5815477368072060250">"Novo chip inserido"</string>
     <string name="carrier_app_notification_text" msgid="6567057546341958637">"Toque para configurar"</string>
     <string name="time_picker_dialog_title" msgid="9053376764985220821">"Definir hora"</string>
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"O PIN é curto demais. Deve ter pelo menos 4 dígitos."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Tente novamente mais tarde"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visualização em tela cheia"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Para sair, deslize de cima para baixo."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Entendi"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Gire a tela para ter uma visualização melhor"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Abra o app <xliff:g id="NAME">%s</xliff:g> em tela cheia para ter uma melhor visualização"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string>
diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ro/donottranslate-cldr.xml b/core/res/res/values-ro/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index bf82cd8c..e4a6a07 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Codul PIN este prea scurt. Trebuie să aibă cel puțin 4 cifre."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Reîncearcă mai târziu"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vizualizare pe ecran complet"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Pentru a ieși, glisează de sus în jos."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Am înțeles"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotește pentru o previzualizare mai bună"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Deschide <xliff:g id="NAME">%s</xliff:g> pe ecran complet pentru o imagine mai bună"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spațiu privat"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonă"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comun"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spațiu privat"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Conținutul sensibil din notificări a fost ascuns"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conținutul aplicației este ascuns de permiterea accesului la ecran din motive de securitate"</string>
diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index c1900a8..0e98b3f 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1885,7 +1885,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-код должен содержать не менее 4 символов."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Повторите попытку позже."</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Полноэкранный режим"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Чтобы выйти, проведите по экрану сверху вниз."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"ОК"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Поверните, чтобы лучше видеть."</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Откройте приложение \"<xliff:g id="NAME">%s</xliff:g>\" в полноэкранном режиме, чтобы лучше видеть."</string>
@@ -2409,6 +2410,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частное пространство"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонированный"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Совместный"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частное пространство"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Конфиденциальная информация в уведомлении скрыта"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Содержимое приложения исключено из демонстрации экрана в целях безопасности."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index f216ce2..03068fe 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN කුඩා වැඩිය. ඉලක්කම් 4 වත් විය යුතුය."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"පසුව නැවත උත්සාහ කරන්න"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"මුළු තිරය බලමින්"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"ඉවත් වීමට, ඉහළ සිට පහළට ස්වයිප් කරන්න"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"වැටහුණි"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"වඩා හොඳ දසුනක් සඳහා කරකවන්න"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"වඩා හොඳ දසුනක් සඳහා <xliff:g id="NAME">%s</xliff:g> පූර්ණ තිරයේ විවෘත කරන්න"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"රහසිගත අවකාශය"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ක්ලෝන කරන්න"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"වාර්ගික"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"රහසිගත අවකාශය"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"සංවේදී දැනුම්දීම් අන්තර්ගතය සැඟවී ඇත"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ආරක්ෂාව සඳහා යෙදුම් අන්තර්ගතය තිරය බෙදා ගැනීමෙන් සඟවා ඇත"</string>
diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sk/donottranslate-cldr.xml b/core/res/res/values-sk/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 9c2a875..694c55bb 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -159,7 +159,7 @@
     <string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Šifrovanie, upozornenia na nešifrované siete"</string>
     <string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Bol zaznamenaný prístup k identifikátoru zariadenia"</string>
     <string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Sieť v okolí nahrala o <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> jedinečný identifikátor vášho zariadenia (IMSI alebo IMEI), keď ste používali SIM siete <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
-    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"Sieť v okolí nahrala o <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> jedinečný identifikátor vášho zariadenia (IMSI alebo IMEI), keď ste používali SIM siete <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>.\n\nZnamená to, že vaša poloha, aktivita alebo totožnosť bola zaznamenaná. Ide o bežný postup, ale môže to byť problém pre ľudí, ktorí majú obavy o ochranu súkromia."</string>
+    <string name="scIdentifierDisclosureIssueSummary" msgid="7283387338827749276">"O <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g> zaznamenala sieť v okolí pri použití SIM karty <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g> jedinečný identifikátor vášho zariadenia (IMSI alebo IMEI).\n\nZnamená to, že bola zaznamenaná vaša poloha, aktivita alebo totožnosť. Ide o bežný postup, ale pre ľudí, ktorým záleží na ochrane súkromia, to môže byť problém."</string>
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Pripojené k šifrovanej sieti <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"Pripojenie SIM siete <xliff:g id="NETWORK_NAME">%1$s</xliff:g> je teraz zabezpečenejšie"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Pripojené k nešifrovanej sieti"</string>
@@ -1885,7 +1885,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Kód PIN je príliš krátky. Musí mať minimálne 4 číslice."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Skúste to neskôr"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Zobrazenie na celú obrazovku"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Ukončíte potiahnutím zhora nadol."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Dobre"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Otočte zariadenie pre lepšie zobrazenie"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Otvorte <xliff:g id="NAME">%s</xliff:g> na celej obrazovke pre lepšie zobrazenie"</string>
@@ -2040,7 +2041,7 @@
     <string name="pin_specific_target" msgid="7824671240625957415">"Pripnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="unpin_target" msgid="3963318576590204447">"Uvoľniť"</string>
     <string name="unpin_specific_target" msgid="3859828252160908146">"Odopnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="app_info" msgid="6113278084877079851">"Info o aplikácii"</string>
+    <string name="app_info" msgid="6113278084877079851">"Informácie o aplikácii"</string>
     <string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"Spúšťa sa ukážka…"</string>
     <string name="demo_restarting_message" msgid="1160053183701746766">"Resetuje sa zariadenie…"</string>
@@ -2409,6 +2410,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Spoločný"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Súkromný priestor"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivého upozornenia je skrytý"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikácie bol na účely zabezpečenia skrytý v zdieľaní obrazovky"</string>
diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sl/donottranslate-cldr.xml b/core/res/res/values-sl/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index b3530e7..edf80c5 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1885,7 +1885,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN je prekratek. Imeti mora vsaj 4 števke."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Poskusite znova pozneje"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Vklopljen je celozaslonski način"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Zaprete ga tako, da z vrha s prstom povlečete navzdol."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Razumem"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zasukajte za boljši pregled."</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Aplikacijo <xliff:g id="NAME">%s</xliff:g> odprite v celozaslonskem načinu za boljši pregled."</string>
@@ -2409,6 +2410,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Zasebni prostor"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Skupno"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Zasebni prostor"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Občutljiva vsebina obvestila je bila skrita"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Pri deljenju zaslona je vsebina aplikacije skrita zaradi varnosti"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index d577e7b..88c4987 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-i është shumë i shkurtër. Duhet të jetë të paktën 4 shifra."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Provo sërish më vonë"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Po shikon ekranin e plotë"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Për të dalë, rrëshqit nga lart poshtë."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"E kuptova"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rrotullo për një pamje më të mirë"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Hape <xliff:g id="NAME">%s</xliff:g> në ekran të plotë për pamje më të mirë"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Hapësira private"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"I përbashkët"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Hapësira private"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Përmbajtjet delikate të njoftimeve janë fshehur"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Përmbajtja e aplikacionit është fshehur nga ndarja e ekranit për arsye sigurie"</string>
diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sr/donottranslate-cldr.xml b/core/res/res/values-sr/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 301fe24..f620246 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1884,7 +1884,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN је прекратак. Мора да има бар 4 цифре."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Пробајте поново касније"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Приказује се цео екран"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Да бисте изашли, превуците надоле одозго."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Важи"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Ротирајте ради бољег приказа"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Отворите апликацију <xliff:g id="NAME">%s</xliff:g> преко целог екрана да бисте боље видели"</string>
@@ -2408,6 +2409,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватан простор"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонирано"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Заједничко"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватан простор"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Осетљив садржај обавештења је скривен"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Садржај апликације је скривен за дељење садржаја екрана због безбедности"</string>
diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index b3c2063..35c4c71 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Pinkoden är för kort. Måste vara minst fyra siffror."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Försök igen senare"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Visar på fullskärm"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Svep nedåt från skärmens överkant för att avsluta."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Rotera för att få en bättre vy"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Öppna <xliff:g id="NAME">%s</xliff:g> i fullskärmsläget för att få en bättre vy"</string>
@@ -2038,7 +2039,7 @@
     <string name="pin_specific_target" msgid="7824671240625957415">"Fäst <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="unpin_target" msgid="3963318576590204447">"Lossa"</string>
     <string name="unpin_specific_target" msgid="3859828252160908146">"Lossa <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="app_info" msgid="6113278084877079851">"Info om appen"</string>
+    <string name="app_info" msgid="6113278084877079851">"Appinformation"</string>
     <string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"Demo startas …"</string>
     <string name="demo_restarting_message" msgid="1160053183701746766">"Enheten återställs …"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Allmän"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Känsligt aviseringsinnehåll dolt"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av säkerhetsskäl döljs appinnehållet vid skärmdelning"</string>
diff --git a/core/res/res/values-sw/donottranslate-cldr.xml b/core/res/res/values-sw/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index f15796e..dc19026 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN ni fupi mno. Lazima iwe angalau tarakimu 4."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Jaribu tena baadaye"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Unatazama skrini nzima"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Ili kuondoka, telezesha kidole kutoka juu hadi chini."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Nimeelewa"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zungusha ili upate mwonekano bora"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Fungua <xliff:g id="NAME">%s</xliff:g> kwenye skrini nzima ili uone maudhui kwa urahisi"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Sehemu ya faragha"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nakala"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wasifu wa pamoja"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Sehemu ya faragha"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Maudhui nyeti kwenye arifa yamefichwa"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Maudhui ya programu yamefichwa ili yasionekane kwenye skrini ya pamoja kwa sababu za kiusalama"</string>
diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml
index 4c70ea3..4aed94c 100644
--- a/core/res/res/values-sw600dp/dimens.xml
+++ b/core/res/res/values-sw600dp/dimens.xml
@@ -112,7 +112,7 @@
     <dimen name="keyguard_muliuser_selector_margin">12dp</dimen>
 
     <!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
-    <dimen name="immersive_mode_cling_width">380dp</dimen>
+    <dimen name="immersive_mode_cling_width">600dp</dimen>
 
     <dimen name="floating_toolbar_preferred_width">544dp</dimen>
 
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 0f2805d..91ae904 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"பின் மிகவும் சிறியதாக உள்ளது. குறைந்தது 4 இலக்கங்கள் இருக்க வேண்டும்."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"மீண்டும் முயற்சிக்கவும்"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"முழுத் திரையில் காட்டுகிறது"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"வெளியேற, மேலிருந்து கீழே ஸ்வைப் செய்யவும்"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"புரிந்தது"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"சிறந்த காட்சிக்கு சுழற்றுங்கள்"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"சிறந்த காட்சிக்கு, <xliff:g id="NAME">%s</xliff:g> ஆப்ஸை முழுத்திரைப் பயன்முறையில் திறக்கவும்"</string>
@@ -2396,7 +2397,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"கீபோர்டு தளவமைப்பு <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… ஆகிய மொழிகளில் அமைக்கப்பட்டது, மாற்ற தட்டவும்."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"கீபோர்டுகள் உள்ளமைக்கப்பட்டன"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"கீபோர்டுகளைப் பார்க்க தட்டவும்"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"தனிப்பட்டது"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"இரகசியமானவை"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"குளோன்"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"பணி"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"பணி 2"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ரகசிய இடம்"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"குளோன்"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"பொது"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ரகசிய இடம்"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"உணர்வுபூர்வமான அறிவிப்பு உள்ளடக்கம் மறைக்கப்பட்டது"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"பாதுகாப்பிற்காக, திரைப் பகிர்வில் இருந்து ஆப்ஸ் உள்ளடக்கம் மறைக்கப்பட்டுள்ளது"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index b448413..759c6e6 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1301,7 +1301,7 @@
     <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"గేమ్‌ను ఎంచుకోండి"</string>
     <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"మెరుగైన పనితీరు పొందడానికి, ఈ గేమ్‌లలో ఒకసారికి ఒక్కటి మాత్రమే తెరవగలరు."</string>
     <string name="old_app_action" msgid="725331621042848590">"<xliff:g id="OLD_APP">%1$s</xliff:g>కి తిరిగి వెళ్లు"</string>
-    <string name="new_app_action" msgid="547772182913269801">"<xliff:g id="NEW_APP">%1$s</xliff:g>ని తెరువు"</string>
+    <string name="new_app_action" msgid="547772182913269801">"<xliff:g id="NEW_APP">%1$s</xliff:g>‌ను తెరవండి"</string>
     <string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> సేవ్ చేయకుండానే మూసివేయబడుతుంది"</string>
     <string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> మెమరీ పరిమితిని మించిపోయింది"</string>
     <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> హీప్ డంప్ సిద్ధంగా ఉంది"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"పిన్‌ చాలా చిన్నదిగా ఉంది. తప్పనిసరిగా కనీసం 4 అంకెలు ఉండాలి."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"తర్వాత మళ్లీ ట్రై చేయండి"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"ఫుల్-స్క్రీన్‌లో వీక్షిస్తున్నారు"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"నిష్క్రమించడానికి, పై నుండి క్రిందికి స్వైప్ చేయండి."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"అర్థమైంది"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"మెరుగైన వీక్షణ కోసం తిప్పండి"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"మెరుగైన వీక్షణ కోసం <xliff:g id="NAME">%s</xliff:g>‌ను ఫుల్ స్క్రీన్‌లో తెరవండి"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ప్రైవేట్ స్పేస్"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"క్లోన్"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"కమ్యూనల్"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ప్రైవేట్ స్పేస్"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"గోప్యమైన నోటిఫికేషన్ కంటెంట్ దాచబడింది"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"సెక్యూరిటీ కోసం స్క్రీన్ షేర్ నుండి యాప్ కంటెంట్ దాచబడింది"</string>
diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-th/donottranslate-cldr.xml b/core/res/res/values-th/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index e34b526..282f66e 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN สั้นเกินไป ต้องมีอย่างน้อย 4 หลัก"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"ลองอีกครั้งในภายหลัง"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"กำลังดูแบบเต็มหน้าจอ"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"หากต้องการออก ให้เลื่อนลงจากด้านบน"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"รับทราบ"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"หมุนเพื่อรับมุมมองที่ดียิ่งขึ้น"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"เปิด <xliff:g id="NAME">%s</xliff:g> ในโหมดเต็มหน้าจอเพื่อรับมุมมองที่ดียิ่งขึ้น"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"พื้นที่ส่วนตัว"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"โคลน"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ส่วนกลาง"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"พื้นที่ส่วนตัว"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"เนื้อหาการแจ้งเตือนที่ละเอียดอ่อนซ่อนอยู่"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ซ่อนเนื้อหาแอปจากการแชร์หน้าจอเพื่อความปลอดภัย"</string>
diff --git a/core/res/res/values-tl/donottranslate-cldr.xml b/core/res/res/values-tl/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 4288460..31b81aa 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Masyadong maikli ang PIN. Hindi dapat mas maikli sa 4 na digit."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Subukang muli sa ibang pagkakataon"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Panonood sa full screen"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Upang lumabas, mag-swipe mula sa itaas pababa."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Nakuha ko"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"I-rotate para sa mas magandang view"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Buksan ang <xliff:g id="NAME">%s</xliff:g> sa full screen para sa mas magandang view"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Pribadong space"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Pribadong space"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Nakatago ang content ng sensitibong notification"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nakatago ang content ng app mula sa pagbabahagi ng screen para sa seguridad"</string>
diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 906ccbc..13f7c2f 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN çok kısa. En az 4 basamaklı olmalı."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Daha sonra tekrar deneyin"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Tam ekran olarak görüntüleme"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Çıkmak için yukarıdan aşağıya doğru hızlıca kaydırın."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Anladım"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Daha iyi bir görünüm elde etmek için ekranı döndürün"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Daha iyi görünüm için <xliff:g id="NAME">%s</xliff:g> uygulamasını açın"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Özel alan"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Paylaşılan"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Özel alan"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Hassas bildirim içerikleri gizlendi"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Uygulama içerikleri, güvenlik nedeniyle ekran paylaşımında gizlendi"</string>
diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-uk/donottranslate-cldr.xml b/core/res/res/values-uk/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index cc9df2e..b8764e7 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1885,7 +1885,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN-код закороткий. Має бути принаймні 4 цифри."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Спробуйте пізніше"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Перегляд на весь екран"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Щоб вийти, проведіть пальцем зверху вниз."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Оберніть для кращого огляду"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Для кращого огляду відкрийте додаток <xliff:g id="NAME">%s</xliff:g> на весь екран"</string>
@@ -2409,6 +2410,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватний простір"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Копія профілю"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Спільний профіль"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватний простір"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Чутливий вміст сповіщення приховано"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"З міркувань безпеки вміст додатка приховано під час показу екрана"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index c0be641..1ae5e20 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"‏PIN کافی چھوٹا ہے۔ کم از کم 4 ہندسے ہونا ضروری ہے۔"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"بعد میں دوبارہ کوشش کریں"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"پوری اسکرین میں دیکھ رہے ہیں"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"خارج ہونے کیلئے اوپر سے نیچے سوائپ کریں۔"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"سمجھ آ گئی"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"بہتر منظر کے لیے گھمائیں"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"بہتر منظر کے لیے <xliff:g id="NAME">%s</xliff:g> کو فُل اسکرین میں کھولیں"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"پرائیویٹ اسپیس"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"کلون"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"کمیونل"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"پرائیویٹ اسپیس"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"حساس اطلاعی مواد چھپا ہوا ہے"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"سیکیورٹی کے مد نظر ایپ کا مواد اسکرین کے اشتراک سے چھپا ہوا ہے"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 517176b..8d6e169 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN kod juda qisqa, kamida 4 ta raqam kiriting."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Keyinroq urinib ko‘ring"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Butun ekranli rejim"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Chiqish uchun tepadan pastga torting."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Yaxshiroq koʻrish uchun kamerani buring"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Yaxshiroq koʻrish uchun butun ekranda <xliff:g id="NAME">%s</xliff:g> ilovasini oching"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Maxfiy makon"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nusxalash"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umumiy"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Maxfiy makon"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Bildirishnomadagi maxfiy axborot berkitildi"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ekran namoyishida xavfsizlik maqsadida ilova kontenti berkitildi"</string>
diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-vi/donottranslate-cldr.xml b/core/res/res/values-vi/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e6db9dd..857a095 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -306,7 +306,7 @@
     <string name="notification_channel_foreground_service" msgid="7102189948158885178">"Các ứng dụng tiêu thụ pin"</string>
     <string name="notification_channel_accessibility_magnification" msgid="1707913872219798098">"Phóng to"</string>
     <string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Việc sử dụng tính năng hỗ trợ tiếp cận"</string>
-    <string name="notification_channel_display" msgid="6905032605735615090">"Màn hình"</string>
+    <string name="notification_channel_display" msgid="6905032605735615090">"Hiển thị"</string>
     <string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang sử dụng pin"</string>
     <string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> ứng dụng đang sử dụng pin"</string>
     <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string>
@@ -1098,8 +1098,8 @@
     <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"Ở lại trang này"</string>
     <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nBạn có chắc chắn muốn điều hướng khỏi trang này không?"</string>
     <string name="autofill_window_title" msgid="4379134104008111961">"Tự động điền với <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
-    <string name="permlab_setAlarm" msgid="1158001610254173567">"đặt báo thức"</string>
-    <string name="permdesc_setAlarm" msgid="2185033720060109640">"Cho phép ứng dụng đặt báo thức trong ứng dụng đồng hồ báo thức được cài đặt. Một số ứng dụng đồng hồ báo thức có thể không thực thi tính  năng này."</string>
+    <string name="permlab_setAlarm" msgid="1158001610254173567">"đặt chuông báo"</string>
+    <string name="permdesc_setAlarm" msgid="2185033720060109640">"Cho phép ứng dụng đặt chuông báo trong ứng dụng đồng hồ được cài đặt. Một số ứng dụng đồng hồ có thể không có năng này."</string>
     <string name="permlab_addVoicemail" msgid="4770245808840814471">"thêm thư thoại"</string>
     <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Cho phép ứng dụng thêm thông báo vào hộp thư thoại đến của bạn."</string>
     <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> đã dán dữ liệu từ bảng nhớ tạm của bạn"</string>
@@ -1329,7 +1329,7 @@
     <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Mặc định (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
     <string name="ringtone_silent" msgid="397111123930141876">"Không"</string>
     <string name="ringtone_picker_title" msgid="667342618626068253">"Nhạc chuông"</string>
-    <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Âm thanh báo thức"</string>
+    <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Âm thanh chuông báo"</string>
     <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Âm thanh thông báo"</string>
     <string name="ringtone_unknown" msgid="5059495249862816475">"Không xác định"</string>
     <string name="wifi_available_sign_in" msgid="381054692557675237">"Đăng nhập vào mạng Wi-Fi"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"Mã PIN quá ngắn. Phải có ít nhất 4 chữ số."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Hãy thử lại sau"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Xem toàn màn hình"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Để thoát, hãy vuốt từ trên cùng xuống dưới."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"OK"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Xoay để xem dễ hơn"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Mở <xliff:g id="NAME">%s</xliff:g> ở chế độ toàn màn hình để xem dễ hơn"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Không gian riêng tư"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nhân bản"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Dùng chung"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Không gian riêng tư"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Đã ẩn nội dung thông báo nhạy cảm"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nội dung ứng dụng bị ẩn khỏi tính năng chia sẻ màn hình vì lý do bảo mật"</string>
diff --git a/core/res/res/values-w204dp-round-watch/dimens_material.xml b/core/res/res/values-w204dp-round-watch/dimens_material.xml
new file mode 100644
index 0000000..c07d5c4
--- /dev/null
+++ b/core/res/res/values-w204dp-round-watch/dimens_material.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="screen_percentage_05">10.2dp</dimen>
+    <dimen name="screen_percentage_10">20.4dp</dimen>
+    <dimen name="screen_percentage_15">30.6dp</dimen>
+</resources>
diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 4b04940..6cd89cf 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -581,7 +581,7 @@
     <string name="permlab_changeTetherState" msgid="9079611809931863861">"更改网络共享连接"</string>
     <string name="permdesc_changeTetherState" msgid="3025129606422533085">"允许应用更改绑定网络连接的状态。"</string>
     <string name="permlab_accessWifiState" msgid="5552488500317911052">"查看WLAN连接"</string>
-    <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看WLAN网络的相关信息,例如是否启用了WLAN以及连接的WLAN设备的名称。"</string>
+    <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看 WLAN 网络的相关信息,例如是否启用了 WLAN 以及连接的 WLAN 设备的名称。"</string>
     <string name="permlab_changeWifiState" msgid="7947824109713181554">"连接WLAN网络和断开连接"</string>
     <string name="permdesc_changeWifiState" msgid="7170350070554505384">"允许该应用与WLAN接入点建立和断开连接,以及更改WLAN网络的设备配置。"</string>
     <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"允许接收WLAN多播"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN码太短,至少应包含4位数字。"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"稍后重试"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"目前处于全屏模式"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"要退出,请从顶部向下滑动。"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"知道了"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"旋转可改善预览效果"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"在全屏模式下打开“<xliff:g id="NAME">%s</xliff:g>”可改善预览效果"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私密空间"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"克隆"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私密空间"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"已隐藏敏感通知内容"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"为安全起见而在屏幕共享画面中处于隐藏状态的应用内容"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 1aa9c72..5224753 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN 碼太短,至少必須為 4 位數。"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"稍後再試"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"開啟全螢幕"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"由頂部向下滑動即可退出。"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"知道了"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"旋轉以改善預覽效果"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"以全螢幕模式開啟「<xliff:g id="NAME">%s</xliff:g>」,以改善預覽效果"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"已隱藏敏感通知內容"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,應用程式內容已從分享螢幕畫面隱藏"</string>
diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index eaed7d2..5c3f819 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1433,7 +1433,7 @@
     <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"輕觸即可選取語言和版面配置"</string>
     <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-    <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"顯示在其他應用程式上層"</string>
+    <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"在其他應用程式上面顯示"</string>
     <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"「<xliff:g id="NAME">%s</xliff:g>」在其他應用程式上顯示內容"</string>
     <string name="alert_windows_notification_title" msgid="6331662751095228536">"「<xliff:g id="NAME">%s</xliff:g>」正在其他應用程式上顯示內容"</string>
     <string name="alert_windows_notification_message" msgid="6538171456970725333">"如果你不想讓「<xliff:g id="NAME">%s</xliff:g>」使用這項功能,請輕觸開啟設定頁面,然後停用此功能。"</string>
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"PIN 長度太短,至少必須為 4 位數。"</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"稍後再試"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"以全螢幕檢視"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"如要退出,請從畫面頂端向下滑動。"</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"知道了"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"旋轉螢幕以瀏覽完整的檢視畫面"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"如要享有更優質的預覽體驗,可透過全螢幕模式開啟「<xliff:g id="NAME">%s</xliff:g>」"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共通"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"系統已隱藏含有私密資訊的通知內容"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,分享螢幕畫面未顯示應用程式內容"</string>
diff --git a/core/res/res/values-zu/donottranslate-cldr.xml b/core/res/res/values-zu/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ba3ae9f..8285d3e 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1883,7 +1883,8 @@
     <string name="restr_pin_error_too_short" msgid="1547007808237941065">"I-PIN yimfushane kakhulu. Okungenani kumele ibe namadijithi angu-4."</string>
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Zama futhi emva kwesikhathi"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Ukubuka isikrini esigcwele"</string>
-    <string name="immersive_cling_description" msgid="7092737175345204832">"Ukuze uphume, swayiphela phansi kusuka phezulu."</string>
+    <!-- no translation found for immersive_cling_description (2896205051090870978) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="7047498036346489883">"Ngiyitholile"</string>
     <string name="display_rotation_camera_compat_toast_after_rotation" msgid="7600891546249829854">"Zungezisa ukuze uthole ukubuka okungcono"</string>
     <string name="display_rotation_camera_compat_toast_in_multi_window" msgid="2473122980393502775">"Vula i-<xliff:g id="NAME">%s</xliff:g> kusikrini esigcwele ngokubuka okungcono"</string>
@@ -2407,6 +2408,7 @@
     <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Indawo engasese"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Yenza i-Clone"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Okomphakathi"</string>
+    <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Indawo engasese"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Okuqukethwe kwesaziso esizwelayo kufihliwe"</string>
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Okuqukethwe kwe-app kufihliwe kusuka ekwabelaneni kwesikrini ngokuvikelwa"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 575573c..df5cbb1 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -37,8 +37,7 @@
         <item>@drawable/fastscroll_label_right_material</item>
         <item>@drawable/fastscroll_thumb_material</item>
         <item>@drawable/fastscroll_track_material</item>
-        <item>@drawable/floating_popup_background_dark</item>
-        <item>@drawable/floating_popup_background_light</item>
+        <item>@drawable/floating_popup_background</item>
         <item>@drawable/ic_ab_back_material</item>
         <item>@drawable/ic_ab_back_material_dark</item>
         <item>@drawable/ic_ab_back_material_light</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 405324b..9846b71 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -606,11 +606,9 @@
         <!-- ============ -->
        <eat-comment />
        <attr name="floatingToolbarCloseDrawable" format="reference" />
-       <attr name="floatingToolbarForegroundColor" format="reference|color" />
        <attr name="floatingToolbarItemBackgroundBorderlessDrawable" format="reference" />
        <attr name="floatingToolbarItemBackgroundDrawable" format="reference" />
        <attr name="floatingToolbarOpenDrawable" format="reference" />
-       <attr name="floatingToolbarPopupBackgroundDrawable" format="reference" />
        <attr name="floatingToolbarDividerColor" format="reference" />
 
         <!-- ============ -->
@@ -9702,6 +9700,12 @@
         <attr name="hotSpotY" format="dimension" />
     </declare-styleable>
 
+    <!-- @hide -->
+    <declare-styleable name="PointerIconVectorTheme">
+        <attr name="pointerIconVectorFill" format="color" />
+        <attr name="pointerIconVectorFillInverse" format="color" />
+    </declare-styleable>
+
     <declare-styleable name="Storage">
         <!-- path to mount point for the storage. -->
         <attr name="mountPoint" format="string" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 27b756d..f94c8ab 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -851,7 +851,12 @@
          of the screen.
          <p>This attribute is supported by the <a
             href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>
-            element. -->
+            element.
+         <aside class="note"><b>Note:</b> Device manufacturers can configure devices to override
+            (ignore) this attribute to improve the layout of apps. See
+            <a href="{@docRoot}guide/practices/device-compatibility-mode">
+            Device compatibility mode</a>.
+         </aside> -->
     <attr name="screenOrientation">
         <!-- No preference specified: let the system decide the best
              orientation.  This will either be the orientation selected
@@ -1447,13 +1452,23 @@
          no other apps in multi-window visible on screen (e.g. picture-in-picture) or on other
          displays. Therefore, this flag cannot be used to assure an exclusive resource access.
 
-         <p>NOTE: A task's root activity value is applied to all additional activities launched in
+         <p>A task's root activity value is applied to all additional activities launched in
          the task. That is if the root activity of a task is resizeable then the system will treat
          all other activities in the task as resizeable and will not if the root activity isn't
          resizeable.
 
-         <p>NOTE: The value of {@link android.R.attr#screenOrientation} is ignored for
-         resizeable activities when in multi-window mode before Android 12. -->
+         <aside class="note"><b>Note:</b>
+            <ul>
+                <li>On Android 11 (API level 30) and lower, the value of
+                    {@link android.R.attr#screenOrientation} is ignored for resizeable activities
+                    in multi-window mode.
+                <li>Device manufacturers can configure devices to override (ignore) this attribute
+                    to force apps to resize. The override does not affect the app's support for
+                    multi-window mode. See
+                    <a href="{@docRoot}guide/practices/device-compatibility-mode">
+                    Device compatibility mode</a>.
+            </ul>
+         </aside> -->
     <attr name="resizeableActivity" format="boolean" />
 
     <!-- Indicates that the activity specifically supports the picture-in-picture form of
@@ -1470,27 +1485,37 @@
     <!-- This value indicates the maximum aspect ratio the activity supports. If the app runs on a
          device with a wider aspect ratio, the system automatically letterboxes the app, leaving
          portions of the screen unused so the app can run at its specified maximum aspect ratio.
-         <p>
-         Maximum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
+         <p>Maximum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
          form. For example, if the maximum aspect ratio is 7:3, set value to 2.33.
-         <p>
-         Value needs to be greater or equal to 1.0, otherwise it is ignored.
-         <p>
-         NOTE: This attribute is ignored if the activity has
-         {@link android.R.attr#resizeableActivity} set to true. -->
+         <p>Value needs to be greater or equal to 1.0, otherwise it is ignored.
+         <aside class="note"><b>Note:</b>
+            <ul>
+                <li>This attribute is ignored if the activity has
+                    {@link android.R.attr#resizeableActivity} set to {@code true}.
+                <li>Device manufacturers can configure devices to override (ignore) this attribute
+                    to improve the layout of apps. See
+                    <a href="{@docRoot}guide/practices/device-compatibility-mode">
+                    Device compatibility mode</a>.
+            </ul>
+         </aside> -->
     <attr name="maxAspectRatio" format="float" />
 
     <!-- This value indicates the minimum aspect ratio the activity supports. If the app runs on a
          device with a narrower aspect ratio, the system automatically letterboxes the app, leaving
          portions of the screen unused so the app can run at its specified minimum aspect ratio.
-         <p>
-         Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
-         form. For example, if the minimum aspect ratio is 4:3, set value to 1.33.
-         <p>
-         Value needs to be greater or equal to 1.0, otherwise it is ignored.
-         <p>
-         NOTE: This attribute is ignored if the activity has
-         {@link android.R.attr#resizeableActivity} set to true. -->
+         <p>Minimum aspect ratio, expressed as (longer dimension / shorter dimension) in decimal
+            form. For example, if the minimum aspect ratio is 4:3, set value to 1.33.
+         <p>Value needs to be greater or equal to 1.0, otherwise it is ignored.
+         <aside class="note"><b>Note:</b>
+            <ul>
+                <li>This attribute is ignored if the activity has
+                    {@link android.R.attr#resizeableActivity} set to {@code true}.
+                <li>Device manufacturers can configure devices to override (ignore) this attribute
+                    to improve the layout of apps. See
+                    <a href="{@docRoot}guide/practices/device-compatibility-mode">
+                    Device compatibility mode</a>.
+            </ul>
+         </aside> -->
     <attr name="minAspectRatio" format="float" />
 
     <!-- This value indicates how tasks rooted at this activity will behave in lockTask mode.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4dfe000..4e133de 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6969,9 +6969,17 @@
          Note that, indefinitely repeating vibrations are not allowed as shutdown vibrations. -->
     <string name="config_defaultShutdownVibrationFile" />
 
-    <!-- Whether single finger panning is enabled when magnification is on -->
+    <!-- Whether single finger panning is enabled by default when magnification is on -->
     <bool name="config_enable_a11y_magnification_single_panning">false</bool>
 
+    <!-- Whether the overscroll handler is enabled when fullscreen magnification is on. When true,
+         the magnification will change the scale if the user pans the magnifier horizontally past
+         the edge of the screen, or delegate the touch events to the app if the user pans vertically
+         past the edge. When false, the magnification will delegate the touch events to the app only
+         when the users uses single finger to pan the magnifier past the edge of the screen,
+         otherwise there are no extra actions. -->
+    <bool name="config_enable_a11y_fullscreen_magnification_overscroll_handler">false</bool>
+
     <!-- The file path in which custom vibrations are provided for haptic feedbacks.
          If the device does not specify any such file path here, if the file path specified here
          does not exist, or if the contents of the file does not make up a valid customization
@@ -7076,4 +7084,8 @@
 
     <!-- Whether the system uses auto-suspend mode. -->
     <bool name="config_useAutoSuspend">true</bool>
+
+    <!-- Whether to show GAIA education screen during account login of private space setup.
+         OEM/Partner can explicitly opt to disable the screen. -->
+    <bool name="config_enableGaiaEducationInPrivateSpace">true</bool>
 </resources>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index dcda5d8..e983427 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -312,6 +312,15 @@
     <integer name="config_oem_enabled_satellite_location_fresh_duration">300</integer>
     <java-symbol type="integer" name="config_oem_enabled_satellite_location_fresh_duration" />
 
+    <!-- The time duration in seconds which is used to decide whether satellite is in emergency
+         mode.
+
+         If the duration from the last time when an emergency call is made to the current time is
+         less than or equal to this duration, satellite is considered as in emergency mode.
+         -->
+    <integer name="config_satellite_emergency_mode_duration">300</integer>
+    <java-symbol type="integer" name="config_satellite_emergency_mode_duration" />
+
     <!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
          will not perform handover if the target transport is out of service, or VoPS not
          supported. The network will be torn down on the source transport, and will be
@@ -409,4 +418,10 @@
     <bool name="config_force_phone_globals_creation">false</bool>
     <java-symbol type="bool" name="config_force_phone_globals_creation" />
 
+    <!-- Boolean indicating whether to enable persistent logging via DropBoxManager.
+     Used in persisting SOS/emergency related log messages.
+     -->
+    <bool name="config_dropboxmanager_persistent_logging_enabled">false</bool>
+    <java-symbol type="bool" name="config_dropboxmanager_persistent_logging_enabled" />
+
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a0807ca..5fea515 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -620,6 +620,9 @@
     <!-- width of the border of the magnification thumbnail -->
     <dimen name="accessibility_magnification_thumbnail_container_stroke_width">4dp</dimen>
 
+    <!-- The distance from the edge within which the gesture is considered to be at the edge -->
+    <dimen name="accessibility_fullscreen_magnification_gesture_edge_slop">12dp</dimen>
+
     <!-- The padding ratio of the Accessibility icon foreground drawable -->
     <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>
 
diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 87141c7..b6e8383 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5125,7 +5125,7 @@
     <string name="immersive_cling_title">Viewing full screen</string>
 
     <!-- Cling help message description when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] -->
-    <string name="immersive_cling_description">To exit, swipe down from the top.</string>
+    <string name="immersive_cling_description">To exit, swipe down from the top of your screen</string>
 
     <!-- Cling help message confirmation button when hiding the navigation bar entering immersive mode [CHAR LIMIT=30] -->
     <string name="immersive_cling_positive">Got it</string>
@@ -6473,6 +6473,9 @@
     <!-- Accessibility label for clone profile user type [CHAR LIMIT=30] -->
     <string name="accessibility_label_communal_profile">Communal</string>
 
+    <!-- Label for private space biometric prompt logo description [CHAR LIMIT=30] -->
+    <string name="private_space_biometric_prompt_title">Private space</string>
+
     <!-- Notification message used when a notification's normal message contains sensitive information [CHAR_LIMIT=NOTIF_BODY] -->
     <string name="redacted_notification_message">Sensitive notification content hidden</string>
     <!-- Notification action title used instead of a notification's normal title sensitive [CHAR_LIMIT=NOTIF_BODY] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index a46dc04..50c3b1a 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1497,6 +1497,36 @@
     </style>
 
     <!-- @hide -->
+    <style name="PointerIconVectorStyleFillBlack">
+        <item name="pointerIconVectorFill">@color/black</item>
+        <item name="pointerIconVectorFillInverse">@color/white</item>
+    </style>
+
+    <!-- @hide -->
+    <style name="PointerIconVectorStyleFillGreen">
+        <item name="pointerIconVectorFill">#6DD58C</item>
+        <item name="pointerIconVectorFillInverse">#6DD58C</item>
+    </style>
+
+    <!-- @hide -->
+    <style name="PointerIconVectorStyleFillYellow">
+        <item name="pointerIconVectorFill">#FDD663</item>
+        <item name="pointerIconVectorFillInverse">#FDD663</item>
+    </style>
+
+    <!-- @hide -->
+    <style name="PointerIconVectorStyleFillPink">
+        <item name="pointerIconVectorFill">#F2B8B5</item>
+        <item name="pointerIconVectorFillInverse">#F2B8B5</item>
+    </style>
+
+    <!-- @hide -->
+    <style name="PointerIconVectorStyleFillBlue">
+        <item name="pointerIconVectorFill">#8AB4F8</item>
+        <item name="pointerIconVectorFillInverse">#8AB4F8</item>
+    </style>
+
+    <!-- @hide -->
     <style name="aerr_list_item" parent="Widget.Material.Light.Button.Borderless">
         <item name="minHeight">?attr/listPreferredItemHeightSmall</item>
         <item name="textAppearance">?attr/textAppearanceListItemSmall</item>
@@ -1690,4 +1720,12 @@
            parent="@style/Theme.DeviceDefault.Resolver">
         <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
+
+    <!--
+        TODO(b/309578419): Make activities go edge-to-edge properly and then remove this.
+    -->
+    <style name="GrantCredentialsPermissionActivity"
+           parent="@style/Theme.DeviceDefault.Light.DialogWhenLarge">
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+    </style>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bb73934..7251d74 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -521,6 +521,7 @@
   <java-symbol type="bool" name="config_preferKeepClearForFocus" />
   <java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/>
   <java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/>
+  <java-symbol type="bool" name="config_enableGaiaEducationInPrivateSpace"/>
 
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
@@ -1116,6 +1117,7 @@
   <java-symbol type="string" name="accessibility_label_private_profile" />
   <java-symbol type="string" name="accessibility_label_clone_profile" />
   <java-symbol type="string" name="accessibility_label_communal_profile" />
+  <java-symbol type="string" name="private_space_biometric_prompt_title" />
   <java-symbol type="string" name="mediasize_unknown_portrait" />
   <java-symbol type="string" name="mediasize_unknown_landscape" />
   <java-symbol type="string" name="mediasize_iso_a0" />
@@ -1617,6 +1619,7 @@
   <java-symbol type="layout" name="restrictions_pin_challenge" />
   <java-symbol type="layout" name="restrictions_pin_setup" />
   <java-symbol type="layout" name="immersive_mode_cling" />
+  <java-symbol type="id" name="immersive_cling_icon" />
   <java-symbol type="layout" name="user_switching_dialog" />
   <java-symbol type="layout" name="common_tab_settings" />
   <java-symbol type="layout" name="resolver_list_per_profile" />
@@ -1694,6 +1697,12 @@
   <java-symbol type="style" name="Pointer" />
   <java-symbol type="style" name="LargePointer" />
   <java-symbol type="style" name="VectorPointer" />
+  <java-symbol type="style" name="PointerIconVectorStyleFillBlack" />
+  <java-symbol type="style" name="PointerIconVectorStyleFillGreen" />
+  <java-symbol type="style" name="PointerIconVectorStyleFillYellow" />
+  <java-symbol type="style" name="PointerIconVectorStyleFillPink" />
+  <java-symbol type="style" name="PointerIconVectorStyleFillBlue" />
+  <java-symbol type="attr" name="pointerIconVectorFill" />
   <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Title" />
   <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Info" />
 
@@ -5418,6 +5427,8 @@
   <java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />
 
   <java-symbol type="bool" name="config_enable_a11y_magnification_single_panning" />
+  <java-symbol type="bool" name="config_enable_a11y_fullscreen_magnification_overscroll_handler" />
+  <java-symbol type="dimen" name="accessibility_fullscreen_magnification_gesture_edge_slop" />
 
   <java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />
 
@@ -5503,4 +5514,16 @@
   <java-symbol type="string" name="face_dangling_notification_msg" />
   <java-symbol type="string" name="biometric_dangling_notification_action_set_up" />
   <java-symbol type="string" name="biometric_dangling_notification_action_not_now" />
+
+  <!-- Priority Modes icons -->
+  <java-symbol type="drawable" name="ic_zen_mode_type_bedtime" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_driving" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_immersive" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_managed" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_other" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_schedule_calendar" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_schedule_time" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_theater" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_unknown" />
+
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index bdbf96b..c3d304d 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -391,11 +391,9 @@
 
         <!-- Floating toolbar styles -->
         <item name="floatingToolbarCloseDrawable">@drawable/ic_ab_back_material_dark</item>
-        <item name="floatingToolbarForegroundColor">@color/foreground_material_dark</item>
         <item name="floatingToolbarItemBackgroundBorderlessDrawable">@drawable/item_background_borderless_material_dark</item>
         <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_dark</item>
         <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_dark</item>
-        <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_dark</item>
         <item name="floatingToolbarDividerColor">@color/floating_popup_divider_dark</item>
 
         <!-- SearchView attributes -->
@@ -579,11 +577,9 @@
 
         <!-- Floating toolbar styles -->
         <item name="floatingToolbarCloseDrawable">@drawable/ic_ab_back_material_light</item>
-        <item name="floatingToolbarForegroundColor">@color/foreground_material_light</item>
         <item name="floatingToolbarItemBackgroundBorderlessDrawable">@drawable/item_background_borderless_material_light</item>
         <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_light</item>
         <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_light</item>
-        <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_light</item>
         <item name="floatingToolbarDividerColor">@color/floating_popup_divider_light</item>
 
         <!-- Tooltip popup colors -->
diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp
index 512a2eb..ed82765 100644
--- a/core/sysprop/Android.bp
+++ b/core/sysprop/Android.bp
@@ -43,3 +43,10 @@
     property_owner: "Platform",
     api_packages: ["android.sysprop"],
 }
+
+sysprop_library {
+    name: "com.android.sysprop.view",
+    srcs: ["ViewProperties.sysprop"],
+    property_owner: "Platform",
+    api_packages: ["android.sysprop"],
+}
diff --git a/core/sysprop/ViewProperties.sysprop b/core/sysprop/ViewProperties.sysprop
new file mode 100644
index 0000000..e801643
--- /dev/null
+++ b/core/sysprop/ViewProperties.sysprop
@@ -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.
+
+module: "android.sysprop.ViewProperties"
+owner: Platform
+
+# On low-end devices, the cost of calculating frame rate can
+# have noticeable overhead. These devices don't benefit from
+# reduced frame rate as much as they benefit from reduced
+# work. By setting this to false, the device won't do any
+# VRR frame rate calculation for Views.
+prop {
+    api_name: "vrr_enabled"
+    type: Boolean
+    prop_name: "ro.view.vrr.enabled"
+    scope: Internal
+    access: Readonly
+}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
index 4f9b269..4c3d4e3 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioManagerTest.java
@@ -16,8 +16,6 @@
 
 package android.hardware.radio;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -36,6 +34,8 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -155,6 +155,9 @@
     private RadioManager mRadioManager;
     private final ApplicationInfo mApplicationInfo = new ApplicationInfo();
 
+    @Rule
+    public final Expect mExpect = Expect.create();
+
     @Mock
     private IRadioService mRadioServiceMock;
     @Mock
@@ -175,7 +178,7 @@
                 () -> new RadioManager.AmBandDescriptor(REGION, /* type= */ 100, AM_LOWER_LIMIT,
                         AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED));
 
-        assertWithMessage("Unsupported band type exception")
+        mExpect.withMessage("Unsupported band type exception")
                 .that(thrown).hasMessageThat().contains("Unsupported band");
     }
 
@@ -183,7 +186,7 @@
     public void getType_forBandDescriptor() {
         RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
 
-        assertWithMessage("AM Band Descriptor type")
+        mExpect.withMessage("AM Band Descriptor type")
                 .that(bandDescriptor.getType()).isEqualTo(RadioManager.BAND_AM);
     }
 
@@ -191,7 +194,7 @@
     public void getRegion_forBandDescriptor() {
         RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
 
-        assertWithMessage("FM Band Descriptor region")
+        mExpect.withMessage("FM Band Descriptor region")
                 .that(bandDescriptor.getRegion()).isEqualTo(REGION);
     }
 
@@ -199,7 +202,7 @@
     public void getLowerLimit_forBandDescriptor() {
         RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
 
-        assertWithMessage("FM Band Descriptor lower limit")
+        mExpect.withMessage("FM Band Descriptor lower limit")
                 .that(bandDescriptor.getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
     }
 
@@ -207,7 +210,7 @@
     public void getUpperLimit_forBandDescriptor() {
         RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
 
-        assertWithMessage("AM Band Descriptor upper limit")
+        mExpect.withMessage("AM Band Descriptor upper limit")
                 .that(bandDescriptor.getUpperLimit()).isEqualTo(AM_UPPER_LIMIT);
     }
 
@@ -215,7 +218,7 @@
     public void getSpacing_forBandDescriptor() {
         RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
 
-        assertWithMessage("AM Band Descriptor spacing")
+        mExpect.withMessage("AM Band Descriptor spacing")
                 .that(bandDescriptor.getSpacing()).isEqualTo(AM_SPACING);
     }
 
@@ -223,7 +226,7 @@
     public void describeContents_forBandDescriptor() {
         RadioManager.BandDescriptor bandDescriptor = createFmBandDescriptor();
 
-        assertWithMessage("Band Descriptor contents")
+        mExpect.withMessage("Band Descriptor contents")
                 .that(bandDescriptor.describeContents()).isEqualTo(0);
     }
 
@@ -237,7 +240,7 @@
 
         RadioManager.BandDescriptor bandDescriptorFromParcel =
                 RadioManager.BandDescriptor.CREATOR.createFromParcel(parcel);
-        assertWithMessage("Band Descriptor created from parcel")
+        mExpect.withMessage("Band Descriptor created from parcel")
                 .that(bandDescriptorFromParcel).isEqualTo(bandDescriptor);
     }
 
@@ -246,14 +249,14 @@
         RadioManager.BandDescriptor[] bandDescriptors =
                 RadioManager.BandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
+        mExpect.withMessage("Band Descriptors").that(bandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
     public void isAmBand_forAmBandDescriptor_returnsTrue() {
         RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
 
-        assertWithMessage("Is AM Band Descriptor an AM band")
+        mExpect.withMessage("Is AM Band Descriptor an AM band")
                 .that(bandDescriptor.isAmBand()).isTrue();
     }
 
@@ -261,43 +264,43 @@
     public void isFmBand_forAmBandDescriptor_returnsFalse() {
         RadioManager.BandDescriptor bandDescriptor = createAmBandDescriptor();
 
-        assertWithMessage("Is AM Band Descriptor an FM band")
+        mExpect.withMessage("Is AM Band Descriptor an FM band")
                 .that(bandDescriptor.isFmBand()).isFalse();
     }
 
     @Test
     public void isStereoSupported_forFmBandDescriptor() {
-        assertWithMessage("FM Band Descriptor stereo")
+        mExpect.withMessage("FM Band Descriptor stereo")
                 .that(FM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED);
     }
 
     @Test
     public void isRdsSupported_forFmBandDescriptor() {
-        assertWithMessage("FM Band Descriptor RDS or RBDS")
+        mExpect.withMessage("FM Band Descriptor RDS or RBDS")
                 .that(FM_BAND_DESCRIPTOR.isRdsSupported()).isEqualTo(RDS_SUPPORTED);
     }
 
     @Test
     public void isTaSupported_forFmBandDescriptor() {
-        assertWithMessage("FM Band Descriptor traffic announcement")
+        mExpect.withMessage("FM Band Descriptor traffic announcement")
                 .that(FM_BAND_DESCRIPTOR.isTaSupported()).isEqualTo(TA_SUPPORTED);
     }
 
     @Test
     public void isAfSupported_forFmBandDescriptor() {
-        assertWithMessage("FM Band Descriptor alternate frequency")
+        mExpect.withMessage("FM Band Descriptor alternate frequency")
                 .that(FM_BAND_DESCRIPTOR.isAfSupported()).isEqualTo(AF_SUPPORTED);
     }
 
     @Test
     public void isEaSupported_forFmBandDescriptor() {
-        assertWithMessage("FM Band Descriptor emergency announcement")
+        mExpect.withMessage("FM Band Descriptor emergency announcement")
                 .that(FM_BAND_DESCRIPTOR.isEaSupported()).isEqualTo(EA_SUPPORTED);
     }
 
     @Test
     public void describeContents_forFmBandDescriptor() {
-        assertWithMessage("FM Band Descriptor contents")
+        mExpect.withMessage("FM Band Descriptor contents")
                 .that(FM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0);
     }
 
@@ -310,7 +313,7 @@
 
         RadioManager.FmBandDescriptor fmBandDescriptorFromParcel =
                 RadioManager.FmBandDescriptor.CREATOR.createFromParcel(parcel);
-        assertWithMessage("FM Band Descriptor created from parcel")
+        mExpect.withMessage("FM Band Descriptor created from parcel")
                 .that(fmBandDescriptorFromParcel).isEqualTo(FM_BAND_DESCRIPTOR);
     }
 
@@ -319,19 +322,19 @@
         RadioManager.FmBandDescriptor[] fmBandDescriptors =
                 RadioManager.FmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("FM Band Descriptors")
+        mExpect.withMessage("FM Band Descriptors")
                 .that(fmBandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
     public void isStereoSupported_forAmBandDescriptor() {
-        assertWithMessage("AM Band Descriptor stereo")
+        mExpect.withMessage("AM Band Descriptor stereo")
                 .that(AM_BAND_DESCRIPTOR.isStereoSupported()).isEqualTo(STEREO_SUPPORTED);
     }
 
     @Test
     public void describeContents_forAmBandDescriptor() {
-        assertWithMessage("AM Band Descriptor contents")
+        mExpect.withMessage("AM Band Descriptor contents")
                 .that(AM_BAND_DESCRIPTOR.describeContents()).isEqualTo(0);
     }
 
@@ -344,7 +347,7 @@
 
         RadioManager.AmBandDescriptor amBandDescriptorFromParcel =
                 RadioManager.AmBandDescriptor.CREATOR.createFromParcel(parcel);
-        assertWithMessage("FM Band Descriptor created from parcel")
+        mExpect.withMessage("FM Band Descriptor created from parcel")
                 .that(amBandDescriptorFromParcel).isEqualTo(AM_BAND_DESCRIPTOR);
     }
 
@@ -353,7 +356,7 @@
         RadioManager.AmBandDescriptor[] amBandDescriptors =
                 RadioManager.AmBandDescriptor.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("AM Band Descriptors")
+        mExpect.withMessage("AM Band Descriptors")
                 .that(amBandDescriptors).hasLength(CREATOR_ARRAY_SIZE);
     }
 
@@ -361,7 +364,7 @@
     public void equals_withSameFmBandDescriptors_returnsTrue() {
         RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor();
 
-        assertWithMessage("The same FM Band Descriptor")
+        mExpect.withMessage("The same FM Band Descriptor")
                 .that(FM_BAND_DESCRIPTOR).isEqualTo(fmBandDescriptorCompared);
     }
 
@@ -369,19 +372,19 @@
     public void equals_withSameAmBandDescriptors_returnsTrue() {
         RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor();
 
-        assertWithMessage("The same AM Band Descriptor")
+        mExpect.withMessage("The same AM Band Descriptor")
                 .that(AM_BAND_DESCRIPTOR).isEqualTo(amBandDescriptorCompared);
     }
 
     @Test
     public void equals_withAmBandDescriptorsAndOtherTypeObject() {
-        assertWithMessage("AM Band Descriptor")
+        mExpect.withMessage("AM Band Descriptor")
                 .that(AM_BAND_DESCRIPTOR).isNotEqualTo(FM_BAND_DESCRIPTOR);
     }
 
     @Test
     public void equals_withFmBandDescriptorsAndOtherTypeObject() {
-        assertWithMessage("FM Band Descriptor")
+        mExpect.withMessage("FM Band Descriptor")
                 .that(FM_BAND_DESCRIPTOR).isNotEqualTo(AM_BAND_DESCRIPTOR);
     }
 
@@ -391,7 +394,7 @@
                 new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
                         AM_UPPER_LIMIT + AM_SPACING, AM_SPACING, STEREO_SUPPORTED);
 
-        assertWithMessage("AM Band Descriptor of different upper limit")
+        mExpect.withMessage("AM Band Descriptor of different upper limit")
                 .that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared);
     }
 
@@ -401,7 +404,7 @@
                 new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
                         AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED);
 
-        assertWithMessage("AM Band Descriptor of different stereo support values")
+        mExpect.withMessage("AM Band Descriptor of different stereo support values")
                 .that(AM_BAND_DESCRIPTOR).isNotEqualTo(amBandDescriptorCompared);
     }
 
@@ -411,7 +414,7 @@
                 REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING * 2,
                 STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
 
-        assertWithMessage("FM Band Descriptors of different support limit values")
+        mExpect.withMessage("FM Band Descriptors of different support limit values")
                 .that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared);
     }
 
@@ -421,7 +424,7 @@
                 REGION + 1, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING,
                 STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
 
-        assertWithMessage("FM Band Descriptors of different region values")
+        mExpect.withMessage("FM Band Descriptors of different region values")
                 .that(FM_BAND_DESCRIPTOR).isNotEqualTo(fmBandDescriptorCompared);
     }
 
@@ -431,7 +434,7 @@
                 REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
                 !STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
 
-        assertWithMessage("FM Band Descriptors of different stereo support values")
+        mExpect.withMessage("FM Band Descriptors of different stereo support values")
                 .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
     }
 
@@ -441,7 +444,7 @@
                 REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
                 STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
 
-        assertWithMessage("FM Band Descriptors of different rds support values")
+        mExpect.withMessage("FM Band Descriptors of different rds support values")
                 .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
     }
 
@@ -451,7 +454,7 @@
                 REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
                 STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED);
 
-        assertWithMessage("FM Band Descriptors of different ta support values")
+        mExpect.withMessage("FM Band Descriptors of different ta support values")
                 .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
     }
 
@@ -461,7 +464,7 @@
                 REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
                 STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED);
 
-        assertWithMessage("FM Band Descriptors of different af support values")
+        mExpect.withMessage("FM Band Descriptors of different af support values")
                 .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
     }
 
@@ -471,7 +474,7 @@
                 REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
                 STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, AF_SUPPORTED, !EA_SUPPORTED);
 
-        assertWithMessage("FM Band Descriptors of different ea support values")
+        mExpect.withMessage("FM Band Descriptors of different ea support values")
                 .that(fmBandDescriptorCompared).isNotEqualTo(FM_BAND_DESCRIPTOR);
     }
 
@@ -479,7 +482,7 @@
     public void hashCode_withSameFmBandDescriptors_equals() {
         RadioManager.FmBandDescriptor fmBandDescriptorCompared = createFmBandDescriptor();
 
-        assertWithMessage("Hash code of the same FM Band Descriptor")
+        mExpect.withMessage("Hash code of the same FM Band Descriptor")
                 .that(fmBandDescriptorCompared.hashCode()).isEqualTo(FM_BAND_DESCRIPTOR.hashCode());
     }
 
@@ -487,7 +490,7 @@
     public void hashCode_withSameAmBandDescriptors_equals() {
         RadioManager.AmBandDescriptor amBandDescriptorCompared = createAmBandDescriptor();
 
-        assertWithMessage("Hash code of the same AM Band Descriptor")
+        mExpect.withMessage("Hash code of the same AM Band Descriptor")
                 .that(amBandDescriptorCompared.hashCode()).isEqualTo(AM_BAND_DESCRIPTOR.hashCode());
     }
 
@@ -497,7 +500,7 @@
                 REGION, RadioManager.BAND_FM, FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING,
                 STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED, !AF_SUPPORTED, EA_SUPPORTED);
 
-        assertWithMessage("Hash code of FM Band Descriptor of different spacing")
+        mExpect.withMessage("Hash code of FM Band Descriptor of different spacing")
                 .that(fmBandDescriptorCompared.hashCode())
                 .isNotEqualTo(FM_BAND_DESCRIPTOR.hashCode());
     }
@@ -508,7 +511,7 @@
                 new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
                         AM_UPPER_LIMIT, AM_SPACING * 2, STEREO_SUPPORTED);
 
-        assertWithMessage("Hash code of AM Band Descriptor of different spacing")
+        mExpect.withMessage("Hash code of AM Band Descriptor of different spacing")
                 .that(amBandDescriptorCompared.hashCode())
                 .isNotEqualTo(AM_BAND_DESCRIPTOR.hashCode());
     }
@@ -517,7 +520,7 @@
     public void getType_forBandConfig() {
         RadioManager.BandConfig fmBandConfig = createFmBandConfig();
 
-        assertWithMessage("FM Band Config type")
+        mExpect.withMessage("FM Band Config type")
                 .that(fmBandConfig.getType()).isEqualTo(RadioManager.BAND_FM);
     }
 
@@ -525,7 +528,7 @@
     public void getRegion_forBandConfig() {
         RadioManager.BandConfig amBandConfig = createAmBandConfig();
 
-        assertWithMessage("AM Band Config region")
+        mExpect.withMessage("AM Band Config region")
                 .that(amBandConfig.getRegion()).isEqualTo(REGION);
     }
 
@@ -533,7 +536,7 @@
     public void getLowerLimit_forBandConfig() {
         RadioManager.BandConfig amBandConfig = createAmBandConfig();
 
-        assertWithMessage("AM Band Config lower limit")
+        mExpect.withMessage("AM Band Config lower limit")
                 .that(amBandConfig.getLowerLimit()).isEqualTo(AM_LOWER_LIMIT);
     }
 
@@ -541,7 +544,7 @@
     public void getUpperLimit_forBandConfig() {
         RadioManager.BandConfig fmBandConfig = createFmBandConfig();
 
-        assertWithMessage("FM Band Config upper limit")
+        mExpect.withMessage("FM Band Config upper limit")
                 .that(fmBandConfig.getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
     }
 
@@ -549,7 +552,7 @@
     public void getSpacing_forBandConfig() {
         RadioManager.BandConfig fmBandConfig = createFmBandConfig();
 
-        assertWithMessage("FM Band Config spacing")
+        mExpect.withMessage("FM Band Config spacing")
                 .that(fmBandConfig.getSpacing()).isEqualTo(FM_SPACING);
     }
 
@@ -557,7 +560,7 @@
     public void describeContents_forBandConfig() {
         RadioManager.BandConfig bandConfig = createFmBandConfig();
 
-        assertWithMessage("FM Band Config contents")
+        mExpect.withMessage("FM Band Config contents")
                 .that(bandConfig.describeContents()).isEqualTo(0);
     }
 
@@ -571,7 +574,7 @@
 
         RadioManager.BandConfig bandConfigFromParcel =
                 RadioManager.BandConfig.CREATOR.createFromParcel(parcel);
-        assertWithMessage("Band Config created from parcel")
+        mExpect.withMessage("Band Config created from parcel")
                 .that(bandConfigFromParcel).isEqualTo(bandConfig);
     }
 
@@ -580,42 +583,42 @@
         RadioManager.BandConfig[] bandConfigs =
                 RadioManager.BandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+        mExpect.withMessage("Band Configs").that(bandConfigs).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
     public void getStereo_forFmBandConfig() {
-        assertWithMessage("FM Band Config stereo")
+        mExpect.withMessage("FM Band Config stereo")
                 .that(FM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED);
     }
 
     @Test
     public void getRds_forFmBandConfig() {
-        assertWithMessage("FM Band Config RDS or RBDS")
+        mExpect.withMessage("FM Band Config RDS or RBDS")
                 .that(FM_BAND_CONFIG.getRds()).isEqualTo(RDS_SUPPORTED);
     }
 
     @Test
     public void getTa_forFmBandConfig() {
-        assertWithMessage("FM Band Config traffic announcement")
+        mExpect.withMessage("FM Band Config traffic announcement")
                 .that(FM_BAND_CONFIG.getTa()).isEqualTo(TA_SUPPORTED);
     }
 
     @Test
     public void getAf_forFmBandConfig() {
-        assertWithMessage("FM Band Config alternate frequency")
+        mExpect.withMessage("FM Band Config alternate frequency")
                 .that(FM_BAND_CONFIG.getAf()).isEqualTo(AF_SUPPORTED);
     }
 
     @Test
     public void getEa_forFmBandConfig() {
-        assertWithMessage("FM Band Config emergency Announcement")
+        mExpect.withMessage("FM Band Config emergency Announcement")
                 .that(FM_BAND_CONFIG.getEa()).isEqualTo(EA_SUPPORTED);
     }
 
     @Test
     public void describeContents_forFmBandConfig() {
-        assertWithMessage("FM Band Config contents")
+        mExpect.withMessage("FM Band Config contents")
                 .that(FM_BAND_CONFIG.describeContents()).isEqualTo(0);
     }
 
@@ -628,7 +631,7 @@
 
         RadioManager.FmBandConfig fmBandConfigFromParcel =
                 RadioManager.FmBandConfig.CREATOR.createFromParcel(parcel);
-        assertWithMessage("FM Band Config created from parcel")
+        mExpect.withMessage("FM Band Config created from parcel")
                 .that(fmBandConfigFromParcel).isEqualTo(FM_BAND_CONFIG);
     }
 
@@ -637,18 +640,18 @@
         RadioManager.FmBandConfig[] fmBandConfigs =
                 RadioManager.FmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+        mExpect.withMessage("FM Band Configs").that(fmBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
     public void getStereo_forAmBandConfig() {
-        assertWithMessage("AM Band Config stereo")
+        mExpect.withMessage("AM Band Config stereo")
                 .that(AM_BAND_CONFIG.getStereo()).isEqualTo(STEREO_SUPPORTED);
     }
 
     @Test
     public void describeContents_forAmBandConfig() {
-        assertWithMessage("AM Band Config contents")
+        mExpect.withMessage("AM Band Config contents")
                 .that(AM_BAND_CONFIG.describeContents()).isEqualTo(0);
     }
 
@@ -661,7 +664,7 @@
 
         RadioManager.AmBandConfig amBandConfigFromParcel =
                 RadioManager.AmBandConfig.CREATOR.createFromParcel(parcel);
-        assertWithMessage("AM Band Config created from parcel")
+        mExpect.withMessage("AM Band Config created from parcel")
                 .that(amBandConfigFromParcel).isEqualTo(AM_BAND_CONFIG);
     }
 
@@ -670,7 +673,7 @@
         RadioManager.AmBandConfig[] amBandConfigs =
                 RadioManager.AmBandConfig.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
+        mExpect.withMessage("AM Band Configs").that(amBandConfigs).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
@@ -679,7 +682,7 @@
                 new RadioManager.FmBandConfig.Builder(FM_BAND_CONFIG);
         RadioManager.FmBandConfig fmBandConfigCompared = builder.build();
 
-        assertWithMessage("The same FM Band Config")
+        mExpect.withMessage("The same FM Band Config")
                 .that(FM_BAND_CONFIG).isEqualTo(fmBandConfigCompared);
     }
 
@@ -690,7 +693,7 @@
                         AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED,
                         TA_SUPPORTED, AF_SUPPORTED, EA_SUPPORTED));
 
-        assertWithMessage("FM Band Config of different regions")
+        mExpect.withMessage("FM Band Config of different regions")
                 .that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared);
     }
 
@@ -701,7 +704,7 @@
                         FM_UPPER_LIMIT, FM_SPACING, !STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
                         AF_SUPPORTED, EA_SUPPORTED));
 
-        assertWithMessage("FM Band Config with different stereo support values")
+        mExpect.withMessage("FM Band Config with different stereo support values")
                 .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
     }
 
@@ -712,7 +715,7 @@
                         FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, !RDS_SUPPORTED, TA_SUPPORTED,
                         AF_SUPPORTED, EA_SUPPORTED));
 
-        assertWithMessage("FM Band Config with different RDS support values")
+        mExpect.withMessage("FM Band Config with different RDS support values")
                 .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
     }
 
@@ -723,7 +726,7 @@
                         FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, !TA_SUPPORTED,
                         AF_SUPPORTED, EA_SUPPORTED));
 
-        assertWithMessage("FM Band Configs with different ta values")
+        mExpect.withMessage("FM Band Configs with different ta values")
                 .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
     }
 
@@ -734,7 +737,7 @@
                 .setTa(TA_SUPPORTED).setAf(!AF_SUPPORTED).setEa(EA_SUPPORTED);
         RadioManager.FmBandConfig fmBandConfigCompared = builder.build();
 
-        assertWithMessage("FM Band Config of different af support value")
+        mExpect.withMessage("FM Band Config of different af support value")
                 .that(FM_BAND_CONFIG).isNotEqualTo(fmBandConfigCompared);
     }
 
@@ -745,19 +748,19 @@
                         FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
                         AF_SUPPORTED, !EA_SUPPORTED));
 
-        assertWithMessage("FM Band Configs with different ea support values")
+        mExpect.withMessage("FM Band Configs with different ea support values")
                 .that(fmBandConfigCompared).isNotEqualTo(FM_BAND_CONFIG);
     }
 
     @Test
     public void equals_withAmBandConfigsAndOtherTypeObject() {
-        assertWithMessage("AM Band Config")
+        mExpect.withMessage("AM Band Config")
                 .that(AM_BAND_CONFIG).isNotEqualTo(FM_BAND_CONFIG);
     }
 
     @Test
     public void equals_withFmBandConfigsAndOtherTypeObject() {
-        assertWithMessage("FM Band Config")
+        mExpect.withMessage("FM Band Config")
                 .that(FM_BAND_CONFIG).isNotEqualTo(AM_BAND_CONFIG);
     }
 
@@ -767,7 +770,7 @@
                 new RadioManager.AmBandConfig.Builder(AM_BAND_CONFIG);
         RadioManager.AmBandConfig amBandConfigCompared = builder.build();
 
-        assertWithMessage("The same AM Band Config")
+        mExpect.withMessage("The same AM Band Config")
                 .that(AM_BAND_CONFIG).isEqualTo(amBandConfigCompared);
     }
 
@@ -777,7 +780,7 @@
                 new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM_HD, AM_LOWER_LIMIT,
                         AM_UPPER_LIMIT, AM_SPACING, STEREO_SUPPORTED));
 
-        assertWithMessage("AM Band Config of different type")
+        mExpect.withMessage("AM Band Config of different type")
                 .that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigCompared);
     }
 
@@ -787,7 +790,7 @@
                 createAmBandDescriptor()).setStereo(!STEREO_SUPPORTED);
         RadioManager.AmBandConfig amBandConfigFromBuilder = builder.build();
 
-        assertWithMessage("AM Band Config of different stereo value")
+        mExpect.withMessage("AM Band Config of different stereo value")
                 .that(AM_BAND_CONFIG).isNotEqualTo(amBandConfigFromBuilder);
     }
 
@@ -795,7 +798,7 @@
     public void hashCode_withSameFmBandConfigs_equals() {
         RadioManager.FmBandConfig fmBandConfigCompared = createFmBandConfig();
 
-        assertWithMessage("Hash code of the same FM Band Config")
+        mExpect.withMessage("Hash code of the same FM Band Config")
                 .that(FM_BAND_CONFIG.hashCode()).isEqualTo(fmBandConfigCompared.hashCode());
     }
 
@@ -803,7 +806,7 @@
     public void hashCode_withSameAmBandConfigs_equals() {
         RadioManager.AmBandConfig amBandConfigCompared = createAmBandConfig();
 
-        assertWithMessage("Hash code of the same AM Band Config")
+        mExpect.withMessage("Hash code of the same AM Band Config")
                 .that(amBandConfigCompared.hashCode()).isEqualTo(AM_BAND_CONFIG.hashCode());
     }
 
@@ -814,7 +817,7 @@
                         FM_UPPER_LIMIT, FM_SPACING, STEREO_SUPPORTED, RDS_SUPPORTED, TA_SUPPORTED,
                         AF_SUPPORTED, EA_SUPPORTED));
 
-        assertWithMessage("Hash code of FM Band Config with different type")
+        mExpect.withMessage("Hash code of FM Band Config with different type")
                 .that(fmBandConfigCompared.hashCode()).isNotEqualTo(FM_BAND_CONFIG.hashCode());
     }
 
@@ -824,87 +827,87 @@
                 new RadioManager.AmBandDescriptor(REGION, RadioManager.BAND_AM, AM_LOWER_LIMIT,
                         AM_UPPER_LIMIT, AM_SPACING, !STEREO_SUPPORTED));
 
-        assertWithMessage("Hash code of AM Band Config with different stereo support")
+        mExpect.withMessage("Hash code of AM Band Config with different stereo support")
                 .that(amBandConfigCompared.hashCode()).isNotEqualTo(AM_BAND_CONFIG.hashCode());
     }
 
     @Test
     public void getId_forModuleProperties() {
-        assertWithMessage("Properties id")
+        mExpect.withMessage("Properties id")
                 .that(AMFM_PROPERTIES.getId()).isEqualTo(PROPERTIES_ID);
     }
 
     @Test
     public void getServiceName_forModuleProperties() {
-        assertWithMessage("Properties service name")
+        mExpect.withMessage("Properties service name")
                 .that(AMFM_PROPERTIES.getServiceName()).isEqualTo(SERVICE_NAME);
     }
 
     @Test
     public void getClassId_forModuleProperties() {
-        assertWithMessage("Properties class ID")
+        mExpect.withMessage("Properties class ID")
                 .that(AMFM_PROPERTIES.getClassId()).isEqualTo(CLASS_ID);
     }
 
     @Test
     public void getImplementor_forModuleProperties() {
-        assertWithMessage("Properties implementor")
+        mExpect.withMessage("Properties implementor")
                 .that(AMFM_PROPERTIES.getImplementor()).isEqualTo(IMPLEMENTOR);
     }
 
     @Test
     public void getProduct_forModuleProperties() {
-        assertWithMessage("Properties product")
+        mExpect.withMessage("Properties product")
                 .that(AMFM_PROPERTIES.getProduct()).isEqualTo(PRODUCT);
     }
 
     @Test
     public void getVersion_forModuleProperties() {
-        assertWithMessage("Properties version")
+        mExpect.withMessage("Properties version")
                 .that(AMFM_PROPERTIES.getVersion()).isEqualTo(VERSION);
     }
 
     @Test
     public void getSerial_forModuleProperties() {
-        assertWithMessage("Serial properties")
+        mExpect.withMessage("Serial properties")
                 .that(AMFM_PROPERTIES.getSerial()).isEqualTo(SERIAL);
     }
 
     @Test
     public void getNumTuners_forModuleProperties() {
-        assertWithMessage("Number of tuners in properties")
+        mExpect.withMessage("Number of tuners in properties")
                 .that(AMFM_PROPERTIES.getNumTuners()).isEqualTo(NUM_TUNERS);
     }
 
     @Test
     public void getNumAudioSources_forModuleProperties() {
-        assertWithMessage("Number of audio sources in properties")
+        mExpect.withMessage("Number of audio sources in properties")
                 .that(AMFM_PROPERTIES.getNumAudioSources()).isEqualTo(NUM_AUDIO_SOURCES);
     }
 
     @Test
     public void isInitializationRequired_forModuleProperties() {
-        assertWithMessage("Initialization required in properties")
+        mExpect.withMessage("Initialization required in properties")
                 .that(AMFM_PROPERTIES.isInitializationRequired())
                 .isEqualTo(IS_INITIALIZATION_REQUIRED);
     }
 
     @Test
     public void isCaptureSupported_forModuleProperties() {
-        assertWithMessage("Capture support in properties")
+        mExpect.withMessage("Capture support in properties")
                 .that(AMFM_PROPERTIES.isCaptureSupported()).isEqualTo(IS_CAPTURE_SUPPORTED);
     }
 
     @Test
     public void isBackgroundScanningSupported_forModuleProperties() {
-        assertWithMessage("Background scan support in properties")
+        mExpect.withMessage("Background scan support in properties")
                 .that(AMFM_PROPERTIES.isBackgroundScanningSupported())
                 .isEqualTo(IS_BG_SCAN_SUPPORTED);
     }
 
     @Test
     public void isProgramTypeSupported_withSupportedType_forModuleProperties() {
-        assertWithMessage("AM/FM frequency type radio support in properties")
+        mExpect.withMessage("AM/FM frequency type radio support in properties")
                 .that(AMFM_PROPERTIES.isProgramTypeSupported(
                         ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY))
                 .isTrue();
@@ -912,28 +915,28 @@
 
     @Test
     public void isProgramTypeSupported_withNonSupportedType_forModuleProperties() {
-        assertWithMessage("DAB frequency type radio support in properties")
+        mExpect.withMessage("DAB frequency type radio support in properties")
                 .that(AMFM_PROPERTIES.isProgramTypeSupported(
                         ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse();
     }
 
     @Test
     public void isProgramIdentifierSupported_withSupportedIdentifier_forModuleProperties() {
-        assertWithMessage("AM/FM frequency identifier radio support in properties")
+        mExpect.withMessage("AM/FM frequency identifier radio support in properties")
                 .that(AMFM_PROPERTIES.isProgramIdentifierSupported(
                         ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)).isTrue();
     }
 
     @Test
     public void isProgramIdentifierSupported_withNonSupportedIdentifier_forModuleProperties() {
-        assertWithMessage("DAB frequency identifier radio support in properties")
+        mExpect.withMessage("DAB frequency identifier radio support in properties")
                 .that(AMFM_PROPERTIES.isProgramIdentifierSupported(
                         ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY)).isFalse();
     }
 
     @Test
     public void getDabFrequencyTable_forModulePropertiesInitializedWithNullTable() {
-        assertWithMessage("Properties DAB frequency table")
+        mExpect.withMessage("Properties DAB frequency table")
                 .that(AMFM_PROPERTIES.getDabFrequencyTable()).isNull();
     }
 
@@ -941,32 +944,32 @@
     public void getDabFrequencyTable_forModulePropertiesInitializedWithEmptyTable() {
         RadioManager.ModuleProperties properties = createAmFmProperties(new ArrayMap<>());
 
-        assertWithMessage("Properties DAB frequency table")
+        mExpect.withMessage("Properties DAB frequency table")
                 .that(properties.getDabFrequencyTable()).isNull();
     }
 
     @Test
     public void getVendorInfo_forModuleProperties() {
-        assertWithMessage("Properties vendor info")
+        mExpect.withMessage("Properties vendor info")
                 .that(AMFM_PROPERTIES.getVendorInfo()).isEmpty();
     }
 
     @Test
     public void getBands_forModuleProperties() {
-        assertWithMessage("Properties bands")
+        mExpect.withMessage("Properties bands")
                 .that(AMFM_PROPERTIES.getBands()).asList()
                 .containsExactly(AM_BAND_DESCRIPTOR, FM_BAND_DESCRIPTOR);
     }
 
     @Test
     public void describeContents_forModuleProperties() {
-        assertWithMessage("Module properties contents")
+        mExpect.withMessage("Module properties contents")
                 .that(AMFM_PROPERTIES.describeContents()).isEqualTo(0);
     }
 
     @Test
     public void toString_forModuleProperties() {
-        assertWithMessage("Module properties string").that(AMFM_PROPERTIES.toString())
+        mExpect.withMessage("Module properties string").that(AMFM_PROPERTIES.toString())
                 .contains(AM_BAND_DESCRIPTOR.toString() + ", " + FM_BAND_DESCRIPTOR.toString());
     }
 
@@ -979,7 +982,7 @@
 
         RadioManager.ModuleProperties modulePropertiesFromParcel =
                 RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel);
-        assertWithMessage("Module properties created from parcel")
+        mExpect.withMessage("Module properties created from parcel")
                 .that(modulePropertiesFromParcel).isEqualTo(AMFM_PROPERTIES);
     }
 
@@ -994,7 +997,7 @@
 
         RadioManager.ModuleProperties modulePropertiesFromParcel =
                 RadioManager.ModuleProperties.CREATOR.createFromParcel(parcel);
-        assertWithMessage("Module properties created from parcel")
+        mExpect.withMessage("Module properties created from parcel")
                 .that(modulePropertiesFromParcel).isEqualTo(propertiesToParcel);
     }
 
@@ -1003,7 +1006,7 @@
         RadioManager.ModuleProperties propertiesCompared =
                 createAmFmProperties(/* dabFrequencyTable= */ null);
 
-        assertWithMessage("The same module properties")
+        mExpect.withMessage("The same module properties")
                 .that(AMFM_PROPERTIES).isEqualTo(propertiesCompared);
     }
 
@@ -1016,7 +1019,7 @@
                 SUPPORTED_PROGRAM_TYPES, SUPPORTED_IDENTIFIERS_TYPES, Map.of("5A", 174928),
                 /* vendorInfo= */ null);
 
-        assertWithMessage("Module properties of different id")
+        mExpect.withMessage("Module properties of different id")
                 .that(AMFM_PROPERTIES).isNotEqualTo(propertiesDab);
     }
 
@@ -1025,7 +1028,7 @@
         RadioManager.ModuleProperties propertiesCompared =
                 createAmFmProperties(/* dabFrequencyTable= */ null);
 
-        assertWithMessage("Hash code of the same module properties")
+        mExpect.withMessage("Hash code of the same module properties")
                 .that(propertiesCompared.hashCode()).isEqualTo(AMFM_PROPERTIES.hashCode());
     }
 
@@ -1034,86 +1037,86 @@
         RadioManager.ModuleProperties[] modulePropertiesArray =
                 RadioManager.ModuleProperties.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("Module properties array")
+        mExpect.withMessage("Module properties array")
                 .that(modulePropertiesArray).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
     public void getSelector_forProgramInfo() {
-        assertWithMessage("Selector of DAB program info")
+        mExpect.withMessage("Selector of DAB program info")
                 .that(DAB_PROGRAM_INFO.getSelector()).isEqualTo(DAB_SELECTOR);
     }
 
     @Test
     public void getLogicallyTunedTo_forProgramInfo() {
-        assertWithMessage("Identifier logically tuned to in DAB program info")
+        mExpect.withMessage("Identifier logically tuned to in DAB program info")
                 .that(DAB_PROGRAM_INFO.getLogicallyTunedTo()).isEqualTo(DAB_SID_EXT_IDENTIFIER);
     }
 
     @Test
     public void getPhysicallyTunedTo_forProgramInfo() {
-        assertWithMessage("Identifier physically tuned to DAB program info")
+        mExpect.withMessage("Identifier physically tuned to DAB program info")
                 .that(DAB_PROGRAM_INFO.getPhysicallyTunedTo()).isEqualTo(DAB_FREQUENCY_IDENTIFIER);
     }
 
     @Test
     public void getRelatedContent_forProgramInfo() {
-        assertWithMessage("DAB program info contents")
+        mExpect.withMessage("DAB program info contents")
                 .that(DAB_PROGRAM_INFO.getRelatedContent())
                 .containsExactly(DAB_SID_EXT_IDENTIFIER_RELATED);
     }
 
     @Test
     public void getChannel_forProgramInfo() {
-        assertWithMessage("Main channel of DAB program info")
+        mExpect.withMessage("Main channel of DAB program info")
                 .that(DAB_PROGRAM_INFO.getChannel()).isEqualTo(0);
     }
 
     @Test
     public void getSubChannel_forProgramInfo() {
-        assertWithMessage("Sub channel of DAB program info")
+        mExpect.withMessage("Sub channel of DAB program info")
                 .that(DAB_PROGRAM_INFO.getSubChannel()).isEqualTo(0);
     }
 
     @Test
     public void isTuned_forProgramInfo() {
-        assertWithMessage("Tuned status of DAB program info")
+        mExpect.withMessage("Tuned status of DAB program info")
                 .that(DAB_PROGRAM_INFO.isTuned()).isTrue();
     }
 
     @Test
     public void isStereo_forProgramInfo() {
-        assertWithMessage("Stereo support in DAB program info")
+        mExpect.withMessage("Stereo support in DAB program info")
                 .that(DAB_PROGRAM_INFO.isStereo()).isTrue();
     }
 
     @Test
     public void isDigital_forProgramInfo() {
-        assertWithMessage("Digital DAB program info")
+        mExpect.withMessage("Digital DAB program info")
                 .that(DAB_PROGRAM_INFO.isDigital()).isTrue();
     }
 
     @Test
     public void isLive_forProgramInfo() {
-        assertWithMessage("Live status of DAB program info")
+        mExpect.withMessage("Live status of DAB program info")
                 .that(DAB_PROGRAM_INFO.isLive()).isTrue();
     }
 
     @Test
     public void isMuted_forProgramInfo() {
-        assertWithMessage("Muted status of DAB program info")
+        mExpect.withMessage("Muted status of DAB program info")
                 .that(DAB_PROGRAM_INFO.isMuted()).isFalse();
     }
 
     @Test
     public void isTrafficProgram_forProgramInfo() {
-        assertWithMessage("Traffic program support in DAB program info")
+        mExpect.withMessage("Traffic program support in DAB program info")
                 .that(DAB_PROGRAM_INFO.isTrafficProgram()).isFalse();
     }
 
     @Test
     public void isTrafficAnnouncementActive_forProgramInfo() {
-        assertWithMessage("Active traffic announcement for DAB program info")
+        mExpect.withMessage("Active traffic announcement for DAB program info")
                 .that(DAB_PROGRAM_INFO.isTrafficAnnouncementActive()).isFalse();
     }
 
@@ -1121,7 +1124,7 @@
     public void isSignalAcquired_forProgramInfo() {
         mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
 
-        assertWithMessage("Signal acquisition status for HD program info")
+        mExpect.withMessage("Signal acquisition status for HD program info")
                 .that(HD_PROGRAM_INFO.isSignalAcquired()).isTrue();
     }
 
@@ -1129,7 +1132,7 @@
     public void isHdSisAvailable_forProgramInfo() {
         mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
 
-        assertWithMessage("SIS information acquisition status for HD program")
+        mExpect.withMessage("SIS information acquisition status for HD program")
                 .that(HD_PROGRAM_INFO.isHdSisAvailable()).isTrue();
     }
 
@@ -1137,31 +1140,31 @@
     public void isHdAudioAvailable_forProgramInfo() {
         mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
 
-        assertWithMessage("Audio acquisition status for HD program")
+        mExpect.withMessage("Audio acquisition status for HD program")
                 .that(HD_PROGRAM_INFO.isHdAudioAvailable()).isFalse();
     }
 
     @Test
     public void getSignalStrength_forProgramInfo() {
-        assertWithMessage("Signal strength of DAB program info")
+        mExpect.withMessage("Signal strength of DAB program info")
                 .that(DAB_PROGRAM_INFO.getSignalStrength()).isEqualTo(SIGNAL_QUALITY);
     }
 
     @Test
     public void getMetadata_forProgramInfo() {
-        assertWithMessage("Metadata of DAB program info")
+        mExpect.withMessage("Metadata of DAB program info")
                 .that(DAB_PROGRAM_INFO.getMetadata()).isEqualTo(METADATA);
     }
 
     @Test
     public void getVendorInfo_forProgramInfo() {
-        assertWithMessage("Vendor info of DAB program info")
+        mExpect.withMessage("Vendor info of DAB program info")
                 .that(DAB_PROGRAM_INFO.getVendorInfo()).isEmpty();
     }
 
     @Test
     public void describeContents_forProgramInfo() {
-        assertWithMessage("Program info contents")
+        mExpect.withMessage("Program info contents")
                 .that(DAB_PROGRAM_INFO.describeContents()).isEqualTo(0);
     }
 
@@ -1170,7 +1173,7 @@
         RadioManager.ProgramInfo[] programInfoArray =
                 RadioManager.ProgramInfo.CREATOR.newArray(CREATOR_ARRAY_SIZE);
 
-        assertWithMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE);
+        mExpect.withMessage("Program infos").that(programInfoArray).hasLength(CREATOR_ARRAY_SIZE);
     }
 
     @Test
@@ -1182,7 +1185,7 @@
 
         RadioManager.ProgramInfo programInfoFromParcel =
                 RadioManager.ProgramInfo.CREATOR.createFromParcel(parcel);
-        assertWithMessage("Program info created from parcel")
+        mExpect.withMessage("Program info created from parcel")
                 .that(programInfoFromParcel).isEqualTo(DAB_PROGRAM_INFO);
     }
 
@@ -1190,7 +1193,7 @@
     public void equals_withSameProgramInfo_returnsTrue() {
         RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(DAB_SELECTOR);
 
-        assertWithMessage("The same program info")
+        mExpect.withMessage("The same program info")
                 .that(dabProgramInfoCompared).isEqualTo(DAB_PROGRAM_INFO);
     }
 
@@ -1202,7 +1205,7 @@
                 /* vendorIds= */ null);
         RadioManager.ProgramInfo dabProgramInfoCompared = createDabProgramInfo(dabSelectorCompared);
 
-        assertWithMessage("Program info with different secondary id selectors")
+        mExpect.withMessage("Program info with different secondary id selectors")
                 .that(DAB_PROGRAM_INFO).isNotEqualTo(dabProgramInfoCompared);
     }
 
@@ -1213,7 +1216,7 @@
 
         mRadioManager.listModules(modules);
 
-        assertWithMessage("Modules in radio manager")
+        mExpect.withMessage("Modules in radio manager")
                 .that(modules).containsExactly(AMFM_PROPERTIES);
     }
 
@@ -1221,7 +1224,7 @@
     public void listModules_forRadioManagerWithNullListAsInput_fails() throws Exception {
         createRadioManager();
 
-        assertWithMessage("Status when listing module with empty list input")
+        mExpect.withMessage("Status when listing module with empty list input")
                 .that(mRadioManager.listModules(null)).isEqualTo(RadioManager.STATUS_BAD_VALUE);
     }
 
@@ -1231,7 +1234,7 @@
         when(mRadioServiceMock.listModules()).thenReturn(null);
         List<RadioManager.ModuleProperties> modules = new ArrayList<>();
 
-        assertWithMessage("Status for listing module when getting null list from HAL client")
+        mExpect.withMessage("Status for listing module when getting null list from HAL client")
                 .that(mRadioManager.listModules(modules)).isEqualTo(RadioManager.STATUS_ERROR);
     }
 
@@ -1241,7 +1244,7 @@
         when(mRadioServiceMock.listModules()).thenThrow(new RemoteException());
         List<RadioManager.ModuleProperties> modules = new ArrayList<>();
 
-        assertWithMessage("Status for listing module when HAL client service is dead")
+        mExpect.withMessage("Status for listing module when HAL client service is dead")
                 .that(mRadioManager.listModules(modules))
                 .isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
     }
@@ -1267,7 +1270,21 @@
         RadioTuner nullTuner = mRadioManager.openTuner(/* moduleId= */ 0, FM_BAND_CONFIG,
                 /* withAudio= */ true, mCallbackMock, /* handler= */ null);
 
-        assertWithMessage("Radio tuner when service is dead").that(nullTuner).isNull();
+        mExpect.withMessage("Radio tuner when service is dead").that(nullTuner).isNull();
+    }
+
+    @Test
+    public void openTuner_withNullCallback() throws Exception {
+        createRadioManager();
+        int moduleId = 0;
+        boolean withAudio = true;
+
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
+                () -> mRadioManager.openTuner(moduleId, FM_BAND_CONFIG, withAudio,
+                        /* callback= */ null, /* handler= */ null));
+
+        mExpect.withMessage("Null tuner callback exception").that(thrown)
+                .hasMessageThat().contains("callback must not be empty");
     }
 
     @Test
@@ -1323,7 +1340,7 @@
         RuntimeException thrown = assertThrows(RuntimeException.class,
                 () -> mRadioManager.addAnnouncementListener(enableTypeSet, mEventListener));
 
-        assertWithMessage("Exception for adding announcement listener with dead service")
+        mExpect.withMessage("Exception for adding announcement listener with dead service")
                 .that(thrown).hasMessageThat().contains(exceptionMessage);
     }
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java
index 20bc8d4..5117d10 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/IRadioServiceHidlImplTest.java
@@ -24,6 +24,8 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -33,6 +35,7 @@
 import android.hardware.radio.ITuner;
 import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.RadioManager;
+import android.os.IBinder;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -132,6 +135,7 @@
     @Test
     public void addAnnouncementListener_forHidlImpl() {
         when(mHal2Mock.hasAnyModules()).thenReturn(true);
+
         ICloseHandle closeHandle = mHidlImpl.addAnnouncementListener(ENABLE_TYPES, mListenerMock);
 
         verify(mHal2Mock).addAnnouncementListener(ENABLE_TYPES, mListenerMock);
@@ -139,4 +143,15 @@
                 .that(closeHandle).isEqualTo(mICloseHandle);
     }
 
+    @Test
+    public void addAnnouncementListener_withoutAnyModules() throws Exception {
+        when(mHal2Mock.hasAnyModules()).thenReturn(false);
+        IBinder binderMock = mock(IBinder.class);
+        when(mListenerMock.asBinder()).thenReturn(binderMock);
+
+        mHidlImpl.addAnnouncementListener(ENABLE_TYPES, mListenerMock);
+
+        verify(mHal2Mock, never()).addAnnouncementListener(ENABLE_TYPES, mListenerMock);
+        verify(binderMock).linkToDeath(any(), anyInt());
+    }
 }
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
index 4f469bb..f3d4ccf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
@@ -112,13 +112,21 @@
             android.hardware.broadcastradio.ProgramSelector hwSel,
             ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
             int hwSignalQuality) {
+        return makeHalProgramInfo(hwSel, logicallyTunedTo, physicallyTunedTo, hwSignalQuality,
+                new ProgramIdentifier[]{}, new Metadata[]{});
+    }
+
+    static ProgramInfo makeHalProgramInfo(
+            android.hardware.broadcastradio.ProgramSelector hwSel,
+            ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
+            int hwSignalQuality, ProgramIdentifier[] relatedContent, Metadata[] metadata) {
         ProgramInfo hwInfo = new ProgramInfo();
         hwInfo.selector = hwSel;
         hwInfo.logicallyTunedTo = logicallyTunedTo;
         hwInfo.physicallyTunedTo = physicallyTunedTo;
         hwInfo.signalQuality = hwSignalQuality;
-        hwInfo.relatedContent = new ProgramIdentifier[]{};
-        hwInfo.metadata = new Metadata[]{};
+        hwInfo.relatedContent = relatedContent;
+        hwInfo.metadata = metadata;
         return hwInfo;
     }
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
index 0e0dbec..2bf0aa3 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java
@@ -16,11 +16,11 @@
 
 package com.android.server.broadcastradio.aidl;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -33,7 +33,10 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -54,6 +57,9 @@
     private AnnouncementAggregator mAnnouncementAggregator;
     private IBinder.DeathRecipient mDeathRecipient;
 
+    @Rule
+    public final Expect mExpect = Expect.create();
+
     @Mock
     private IAnnouncementListener mListenerMock;
     @Mock
@@ -75,6 +81,18 @@
     }
 
     @Test
+    public void constructor_withBinderDied() throws Exception {
+        RemoteException remoteException = new RemoteException("Binder is died");
+        doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt());
+
+        RuntimeException thrown = assertThrows(RuntimeException.class, () ->
+                new AnnouncementAggregator(mListenerMock, mLock));
+
+        mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat()
+                .contains(remoteException.getMessage());
+    }
+
+    @Test
     public void onListUpdated_withOneModuleWatcher() throws Exception {
         ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
                 ArgumentCaptor.forClass(IAnnouncementListener.class);
@@ -103,7 +121,7 @@
             moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));
 
             verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
-            assertWithMessage("Number of announcements %s after %s announcements were updated",
+            mExpect.withMessage("Number of announcements %s after %s announcements were updated",
                     announcementsCaptor.getValue(), index + 1)
                     .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
         }
@@ -131,7 +149,7 @@
                 () -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0],
                         TEST_ENABLED_TYPES));
 
-        assertWithMessage("Exception for watching module after aggregator has been closed")
+        mExpect.withMessage("Exception for watching module after aggregator has been closed")
                 .that(thrown).hasMessageThat()
                 .contains("announcement aggregator has already been closed");
     }
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index 8d9fad9..5a08acd 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -27,6 +27,7 @@
 import android.hardware.broadcastradio.DabTableEntry;
 import android.hardware.broadcastradio.IdentifierType;
 import android.hardware.broadcastradio.Metadata;
+import android.hardware.broadcastradio.ProgramFilter;
 import android.hardware.broadcastradio.ProgramIdentifier;
 import android.hardware.broadcastradio.ProgramInfo;
 import android.hardware.broadcastradio.Properties;
@@ -41,6 +42,8 @@
 import android.hardware.radio.UniqueProgramIdentifier;
 import android.os.ServiceSpecificException;
 import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
 import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
@@ -52,6 +55,7 @@
 import org.junit.Test;
 import org.mockito.stubbing.Answer;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -84,6 +88,7 @@
 
     private static final int TEST_SIGNAL_QUALITY = 1;
     private static final long TEST_DAB_DMB_SID_EXT_VALUE = 0xA000000111L;
+    private static final long TEST_DAB_SID_EXT_LEGACY_VALUE = 0xA00111L;
     private static final long TEST_DAB_ENSEMBLE_VALUE = 0x1001;
     private static final long TEST_DAB_FREQUENCY_VALUE = 220_352;
     private static final long TEST_FM_FREQUENCY_VALUE = 92_100;
@@ -93,9 +98,17 @@
     private static final long TEST_HD_LOCATION_VALUE =  0x4E647007665CF6L;
     private static final long TEST_VENDOR_ID_VALUE = 9_901;
 
+    private static final ProgramSelector.Identifier TEST_INVALID_ID =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_INVALID, 1);
+    private static final ProgramIdentifier TEST_HAL_INVALID_ID =
+            AidlTestUtils.makeHalIdentifier(IdentifierType.INVALID, 1);
+
     private static final ProgramSelector.Identifier TEST_DAB_SID_EXT_ID =
             new ProgramSelector.Identifier(
                     ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, TEST_DAB_DMB_SID_EXT_VALUE);
+    private static final ProgramSelector.Identifier TEST_DAB_SID_EXT_LEGACY_ID =
+            new ProgramSelector.Identifier(
+                    ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT, TEST_DAB_SID_EXT_LEGACY_VALUE);
     private static final ProgramSelector.Identifier TEST_DAB_ENSEMBLE_ID =
             new ProgramSelector.Identifier(
                     ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, TEST_DAB_ENSEMBLE_VALUE);
@@ -118,6 +131,10 @@
             ProgramSelector.PROGRAM_TYPE_DAB, TEST_DAB_SID_EXT_ID,
             new ProgramSelector.Identifier[]{TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID},
             /* vendorIds= */ null);
+    private static final ProgramSelector TEST_DAB_SELECTOR_LEGACY = new ProgramSelector(
+            ProgramSelector.PROGRAM_TYPE_DAB, TEST_DAB_SID_EXT_LEGACY_ID,
+            new ProgramSelector.Identifier[]{TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID},
+            /* vendorIds= */ null);
     private static final ProgramSelector TEST_FM_SELECTOR =
             AidlTestUtils.makeFmSelector(TEST_FM_FREQUENCY_VALUE);
 
@@ -125,6 +142,9 @@
             new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT,
                     TEST_HD_STATION_ID_EXT_VALUE);
 
+    private static final ProgramIdentifier TEST_HAL_HD_STATION_EXT_ID =
+            AidlTestUtils.makeHalIdentifier(IdentifierType.HD_STATION_ID_EXT,
+                    TEST_HD_STATION_ID_EXT_VALUE);
     private static final ProgramIdentifier TEST_HAL_HD_STATION_LOCATION_ID =
             AidlTestUtils.makeHalIdentifier(IdentifierType.HD_STATION_LOCATION,
                     TEST_HD_LOCATION_VALUE);
@@ -139,7 +159,7 @@
     private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING;
 
     private static final RadioManager.ModuleProperties MODULE_PROPERTIES =
-            convertToModuleProperties();
+            createModuleProperties();
     private static final Announcement ANNOUNCEMENT =
             ConversionUtils.announcementFromHalAnnouncement(
                     AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY));
@@ -214,6 +234,38 @@
     }
 
     @Test
+    public void throwOnError_withInvalidArgumentException() {
+        ServiceSpecificException halException = new ServiceSpecificException(
+                Result.INVALID_ARGUMENTS);
+
+        RuntimeException thrown = ConversionUtils.throwOnError(halException, "tune");
+
+        expect.withMessage("Exception thrown for invalid argument error")
+                .that(thrown).hasMessageThat().contains("tune: INVALID_ARGUMENTS");
+    }
+
+    @Test
+    public void throwOnError_withTimeoutException() {
+        ServiceSpecificException halException = new ServiceSpecificException(Result.TIMEOUT);
+
+        RuntimeException thrown = ConversionUtils.throwOnError(halException, "seek");
+
+        expect.withMessage("Exception thrown for timeout error")
+                .that(thrown).hasMessageThat().contains("seek: TIMEOUT");
+    }
+
+    @Test
+    public void throwOnError_withUnknownErrorCode() {
+        int invalidErrorCode = 100;
+        ServiceSpecificException halException = new ServiceSpecificException(invalidErrorCode);
+
+        RuntimeException thrown = ConversionUtils.throwOnError(halException, "seek");
+
+        expect.withMessage("Exception thrown for unknown error code")
+                .that(thrown).hasMessageThat().contains("seek: unknown error");
+    }
+
+    @Test
     public void propertiesFromHalProperties_idsMatch() {
         expect.withMessage("Properties id")
                 .that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID);
@@ -255,7 +307,7 @@
         Map<String, Integer> dabTableExpected = Map.of(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1,
                 DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2);
 
-        expect.withMessage("Supported program types")
+        expect.withMessage("DAB frequency table")
                 .that(MODULE_PROPERTIES.getDabFrequencyTable())
                 .containsExactlyEntriesIn(dabTableExpected);
     }
@@ -291,6 +343,37 @@
     }
 
     @Test
+    public void propertiesFromHalProperties_withoutAmFmAndDabConfigs() {
+        RadioManager.ModuleProperties properties = createModuleProperties(/* amFmConfig= */ null,
+                /* dabTableEntries= */ null);
+
+        expect.withMessage("Empty AM/FM config")
+                .that(properties.getBands()).asList().isEmpty();
+        expect.withMessage("Empty DAB config")
+                .that(properties.getDabFrequencyTable()).isNull();
+    }
+
+    @Test
+    public void propertiesFromHalProperties_withInvalidBand() {
+        AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
+        amFmRegionConfig.ranges = new AmFmBandRange[]{createAmFmBandRange(/* lowerBound= */ 50000,
+                /* upperBound= */ 60000, /* spacing= */ 10),
+                createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)};
+
+        RadioManager.ModuleProperties properties = createModuleProperties(amFmRegionConfig,
+                new DabTableEntry[]{});
+
+        RadioManager.BandDescriptor[] bands = properties.getBands();
+        expect.withMessage("Band descriptors").that(bands).hasLength(1);
+        expect.withMessage("FM band frequency lower limit")
+                .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
+        expect.withMessage("FM band frequency upper limit")
+                .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
+        expect.withMessage("FM band frequency spacing")
+                .that(bands[0].getSpacing()).isEqualTo(FM_SPACING);
+    }
+
+    @Test
     public void identifierToHalProgramIdentifier_withDabId() {
         ProgramIdentifier halDabId =
                 ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID);
@@ -358,6 +441,13 @@
     }
 
     @Test
+    public void identifierFromHalProgramIdentifier_withInvalidIdentifier() {
+        expect.withMessage("Identifier converted from invalid HAL identifier")
+                .that(ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_INVALID_ID))
+                .isNull();
+    }
+
+    @Test
     public void programSelectorToHalProgramSelector_withValidSelector() {
         android.hardware.broadcastradio.ProgramSelector halDabSelector =
                 ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR);
@@ -370,6 +460,23 @@
     }
 
     @Test
+    public void programSelectorToHalProgramSelector_withInvalidSecondaryId() {
+        ProgramSelector dabSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB,
+                TEST_DAB_SID_EXT_ID, new ProgramSelector.Identifier[]{TEST_INVALID_ID,
+                    TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID}, /* vendorIds= */ null);
+
+        android.hardware.broadcastradio.ProgramSelector halDabSelector =
+                ConversionUtils.programSelectorToHalProgramSelector(dabSelector);
+
+        expect.withMessage("Primary identifier of converted HAL DAB selector with invalid "
+                        + "secondary id").that(halDabSelector.primaryId)
+                .isEqualTo(TEST_HAL_DAB_SID_EXT_ID);
+        expect.withMessage("Secondary identifiers of converted HAL DAB selector with "
+                        + "invalid secondary id").that(halDabSelector.secondaryIds).asList()
+                .containsExactly(TEST_HAL_DAB_FREQUENCY_ID, TEST_HAL_DAB_ENSEMBLE_ID);
+    }
+
+    @Test
     public void programSelectorFromHalProgramSelector_withValidSelector() {
         android.hardware.broadcastradio.ProgramSelector halDabSelector =
                 AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
@@ -386,6 +493,33 @@
     }
 
     @Test
+    public void programSelectorFromHalProgramSelector_withInvalidSelector() {
+        android.hardware.broadcastradio.ProgramSelector invalidSelector =
+                AidlTestUtils.makeHalSelector(TEST_HAL_INVALID_ID, new ProgramIdentifier[]{});
+
+        expect.withMessage("Selector converted from invalid HAL selector")
+                .that(ConversionUtils.programSelectorFromHalProgramSelector(invalidSelector))
+                .isNull();
+    }
+
+    @Test
+    public void programSelectorFromHalProgramSelector_withInvalidSecondaryId() {
+        android.hardware.broadcastradio.ProgramSelector halDabSelector =
+                AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
+                        TEST_HAL_INVALID_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
+
+        ProgramSelector dabSelector =
+                ConversionUtils.programSelectorFromHalProgramSelector(halDabSelector);
+
+        expect.withMessage("Primary identifier of converted DAB selector with invalid "
+                        + "secondary id").that(dabSelector.getPrimaryId())
+                .isEqualTo(TEST_DAB_SID_EXT_ID);
+        expect.withMessage("Secondary identifiers of converted DAB selector with invalid "
+                        + "secondary id").that(dabSelector.getSecondaryIds()).asList()
+                .containsExactly(TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID);
+    }
+
+    @Test
     public void programInfoFromHalProgramInfo_withValidProgramInfo() {
         android.hardware.broadcastradio.ProgramSelector halDabSelector =
                 AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
@@ -410,6 +544,22 @@
     }
 
     @Test
+    public void programInfoFromHalProgramInfo_withRelatedContent() {
+        android.hardware.broadcastradio.ProgramSelector halDabSelector =
+                AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
+                        TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
+        ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(halDabSelector,
+                TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_FREQUENCY_ID, TEST_SIGNAL_QUALITY,
+                new ProgramIdentifier[]{TEST_HAL_HD_STATION_EXT_ID}, new Metadata[]{});
+
+        RadioManager.ProgramInfo programInfo =
+                ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+
+        expect.withMessage("Related content of converted program info")
+                .that(programInfo.getRelatedContent()).containsExactly(TEST_HD_STATION_EXT_ID);
+    }
+
+    @Test
     public void programInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
         android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
                 AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID,
@@ -469,6 +619,29 @@
     }
 
     @Test
+    public void programInfoMeetsSdkVersionRequirement_withLowerVersionIdForLogicallyTunedTo() {
+        RadioManager.ProgramInfo dabProgramInfo = AidlTestUtils.makeProgramInfo(
+                TEST_DAB_SELECTOR_LEGACY, TEST_DAB_SID_EXT_ID, TEST_DAB_FREQUENCY_ID,
+                TEST_SIGNAL_QUALITY);
+
+        expect.withMessage("Program info %s with logically tuned to ID not of required SDK version",
+                        dabProgramInfo).that(ConversionUtils.programInfoMeetsSdkVersionRequirement(
+                                dabProgramInfo, T_APP_UID)).isFalse();
+    }
+
+    @Test
+    public void programInfoMeetsSdkVersionRequirement_withLowerVersionIdForRelatedContent() {
+        RadioManager.ProgramInfo dabProgramInfo = new RadioManager.ProgramInfo(
+                TEST_DAB_SELECTOR_LEGACY, TEST_DAB_SID_EXT_LEGACY_ID, TEST_DAB_FREQUENCY_ID,
+                List.of(TEST_DAB_SID_EXT_ID), /* infoFlags= */ 0, TEST_SIGNAL_QUALITY,
+                new RadioMetadata.Builder().build(), new ArrayMap<>());
+
+        expect.withMessage("Program info %s with related content not of required SDK version",
+                dabProgramInfo).that(ConversionUtils.programInfoMeetsSdkVersionRequirement(
+                dabProgramInfo, T_APP_UID)).isFalse();
+    }
+
+    @Test
     public void programInfoMeetsSdkVersionRequirement_withRequiredVersionId_returnsTrue() {
         RadioManager.ProgramInfo fmProgramInfo = AidlTestUtils.makeProgramInfo(TEST_FM_SELECTOR,
                 TEST_SIGNAL_QUALITY);
@@ -586,20 +759,122 @@
     }
 
     @Test
-    public void radioMetadataFromHalMetadata_withFlagEnabled() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
-
+    public void radioMetadataFromHalMetadata() {
+        int rdsPtyValue = 3;
+        int rbdsPtyValue = 4;
+        String rdsRtValue = "rdsRtTest";
+        String songAlbumValue = "songAlbumTest";
+        String programNameValue = "programNameTest";
         RadioMetadata convertedMetadata = ConversionUtils.radioMetadataFromHalMetadata(
-                new Metadata[]{TEST_HAL_SONG_TITLE, TEST_HAL_HD_SUBCHANNELS, TEST_HAL_ALBUM_ART});
+                new Metadata[]{TEST_HAL_SONG_TITLE, TEST_HAL_ALBUM_ART,
+                        Metadata.rdsPty(rdsPtyValue), Metadata.rbdsPty(rbdsPtyValue),
+                        Metadata.rdsRt(rdsRtValue), Metadata.songAlbum(songAlbumValue),
+                        Metadata.programName(programNameValue)});
 
-        expect.withMessage("Metadata with flag enabled").that(convertedMetadata.size())
-                .isEqualTo(3);
         expect.withMessage("Song title with flag enabled")
                 .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_TITLE))
                 .isEqualTo(TEST_SONG_TITLE);
         expect.withMessage("Album art with flag enabled")
                 .that(convertedMetadata.getInt(RadioMetadata.METADATA_KEY_ART))
                 .isEqualTo(TEST_ALBUM_ART);
+        expect.withMessage("RDS PTY with flag enabled")
+                .that(convertedMetadata.getInt(RadioMetadata.METADATA_KEY_RDS_PTY))
+                .isEqualTo(rdsPtyValue);
+        expect.withMessage("RBDS PTY with flag enabled")
+                .that(convertedMetadata.getInt(RadioMetadata.METADATA_KEY_RBDS_PTY))
+                .isEqualTo(rbdsPtyValue);
+        expect.withMessage("RDS RT with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_RDS_RT))
+                .isEqualTo(rdsRtValue);
+        expect.withMessage("Album with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_ALBUM))
+                .isEqualTo(songAlbumValue);
+        expect.withMessage("Program name with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_PROGRAM_NAME))
+                .isEqualTo(programNameValue);
+    }
+
+    @Test
+    public void radioMetadataFromHalMetadata_withDabMetadata() {
+        String dabEnsembleNameValue = "dabEnsembleNameTest";
+        String dabEnsembleNameShortValue = "dabEnsembleNameShortTest";
+        String dabServiceNameValue = "dabServiceNameTest";
+        String dabServiceNameShortValue = "dabServiceNameShortTest";
+        String dabComponentNameValue = "dabComponentNameTest";
+        String dabComponentNameShortValue = "dabComponentNameShortTest";
+        RadioMetadata convertedMetadata = ConversionUtils.radioMetadataFromHalMetadata(
+                new Metadata[]{Metadata.dabEnsembleName(dabEnsembleNameValue),
+                        Metadata.dabEnsembleNameShort(dabEnsembleNameShortValue),
+                        Metadata.dabServiceName(dabServiceNameValue),
+                        Metadata.dabServiceNameShort(dabServiceNameShortValue),
+                        Metadata.dabComponentName(dabComponentNameValue),
+                        Metadata.dabComponentNameShort(dabComponentNameShortValue)});
+
+        expect.withMessage("DAB Ensemble name with flag enabled")
+                .that(convertedMetadata.getString(
+                        RadioMetadata.METADATA_KEY_DAB_ENSEMBLE_NAME))
+                .isEqualTo(dabEnsembleNameValue);
+        expect.withMessage("DAB Ensemble short name with flag enabled")
+                .that(convertedMetadata.getString(
+                        RadioMetadata.METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT))
+                .isEqualTo(dabEnsembleNameShortValue);
+        expect.withMessage("DAB service service name with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_DAB_SERVICE_NAME))
+                .isEqualTo(dabServiceNameValue);
+        expect.withMessage("DAB service service short name with flag enabled")
+                .that(convertedMetadata.getString(
+                        RadioMetadata.METADATA_KEY_DAB_SERVICE_NAME_SHORT))
+                .isEqualTo(dabServiceNameShortValue);
+        expect.withMessage("DAB component name with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_DAB_COMPONENT_NAME))
+                .isEqualTo(dabComponentNameValue);
+        expect.withMessage("DAB component short name with flag enabled")
+                .that(convertedMetadata.getString(
+                        RadioMetadata.METADATA_KEY_DAB_COMPONENT_NAME_SHORT))
+                .isEqualTo(dabComponentNameShortValue);
+    }
+
+    @Test
+    public void radioMetadataFromHalMetadata_withHdMedatadataAndFlagEnabled() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_HD_RADIO_IMPROVED);
+        String genreValue = "genreTest";
+        String commentShortDescriptionValue = "commentShortDescriptionTest";
+        String commentActualTextValue = "commentActualTextTest";
+        String commercialValue = "commercialTest";
+        List<String> ufidsValue = List.of("ufids1Test", "ufids2Test");
+        String hdStationNameShortValue = "hdStationNameShortTest";
+        String hdStationNameLongValue = "hdStationNameLongTest";
+        RadioMetadata convertedMetadata = ConversionUtils.radioMetadataFromHalMetadata(
+                new Metadata[]{TEST_HAL_HD_SUBCHANNELS, Metadata.genre(genreValue),
+                        Metadata.commentShortDescription(commentShortDescriptionValue),
+                        Metadata.commentActualText(commentActualTextValue),
+                        Metadata.commercial(commercialValue),
+                        Metadata.ufids(ufidsValue.toArray(new String[0])),
+                        Metadata.hdStationNameShort(hdStationNameShortValue),
+                        Metadata.hdStationNameLong(hdStationNameLongValue)});
+
+        expect.withMessage("Genre with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_GENRE))
+                .isEqualTo(genreValue);
+        expect.withMessage("Short description of comment with flag enabled")
+                .that(convertedMetadata.getString(
+                        RadioMetadata.METADATA_KEY_COMMENT_SHORT_DESCRIPTION))
+                .isEqualTo(commentShortDescriptionValue);
+        expect.withMessage("Actual text of comment with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_COMMENT_ACTUAL_TEXT))
+                .isEqualTo(commentActualTextValue);
+        expect.withMessage("Commercial with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_COMMERCIAL))
+                .isEqualTo(commercialValue);
+        expect.withMessage("UFIDs with flag enabled")
+                .that(convertedMetadata.getStringArray(RadioMetadata.METADATA_KEY_UFIDS)).asList()
+                .containsExactlyElementsIn(ufidsValue);
+        expect.withMessage("HD station short name with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_HD_STATION_NAME_SHORT))
+                .isEqualTo(hdStationNameShortValue);
+        expect.withMessage("HD station long name with flag enabled")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_HD_STATION_NAME_LONG))
+                .isEqualTo(hdStationNameLongValue);
         expect.withMessage("HD sub-channels with flag enabled")
                 .that(convertedMetadata.getInt(RadioMetadata
                         .METADATA_KEY_HD_SUBCHANNELS_AVAILABLE)).isEqualTo(TEST_HD_SUBCHANNELS);
@@ -622,11 +897,76 @@
                 .isEqualTo(TEST_ALBUM_ART);
     }
 
-    private static RadioManager.ModuleProperties convertToModuleProperties() {
+    @Test
+    public void getBands_withInvalidFrequency() {
+        expect.withMessage("Band for invalid frequency")
+                .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(Utils.FrequencyBand.UNKNOWN);
+    }
+
+    @Test
+    public void filterToHalProgramFilter_withNullFilter() {
+        ProgramFilter filter = ConversionUtils.filterToHalProgramFilter(null);
+
+        expect.withMessage("Filter identifier types").that(filter.identifierTypes)
+                .asList().isEmpty();
+        expect.withMessage("Filter identifiers").that(filter.identifiers).asList()
+                .isEmpty();
+    }
+
+    @Test
+    public void filterToHalProgramFilter_withInvalidIdentifier() {
+        Set<ProgramSelector.Identifier> identifiers =
+                new ArraySet<ProgramSelector.Identifier>(2);
+        identifiers.add(TEST_INVALID_ID);
+        identifiers.add(TEST_DAB_SID_EXT_ID);
+        ProgramList.Filter filter = new ProgramList.Filter(/* identifierTypes */ new ArraySet<>(),
+                identifiers, /* includeCategories= */ true, /* excludeModifications= */ false);
+        ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(filter);
+
+        expect.withMessage("Filter identifiers with invalid ones removed")
+                .that(halFilter.identifiers).asList().containsExactly(
+                        ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID));
+    }
+
+    @Test
+    public void vendorInfoToHalVendorKeyValues_withNull() {
+        expect.withMessage("Null vendor info converted to HAL")
+                .that(ConversionUtils.vendorInfoToHalVendorKeyValues(/* info= */ null)).asList()
+                .isEmpty();
+    }
+
+    @Test
+    public void vendorInfoToHalVendorKeyValues_withNullValue() {
+        Map<String, String> vendorInfo = new ArrayMap<>();
+        vendorInfo.put(VENDOR_INFO_KEY_1, null);
+
+        expect.withMessage("Vendor info with null value converted to HAL")
+                .that(ConversionUtils.vendorInfoToHalVendorKeyValues(vendorInfo)).asList()
+                .isEmpty();
+    }
+
+    @Test
+    public void vendorInfoFromHalVendorKeyValues_withNullElements() {
+        VendorKeyValue halVendorInfo = new VendorKeyValue();
+        halVendorInfo.key = null;
+        halVendorInfo.value = VENDOR_INFO_VALUE_1;
+        VendorKeyValue[] halVendorInfoArray = new VendorKeyValue[]{halVendorInfo};
+
+        expect.withMessage("Null vendor info converted from HAL")
+                .that(ConversionUtils.vendorInfoFromHalVendorKeyValues(halVendorInfoArray))
+                .isEmpty();
+    }
+
+    private static RadioManager.ModuleProperties createModuleProperties() {
         AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
         DabTableEntry[] dabTableEntries = new DabTableEntry[]{
                 createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1),
                 createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)};
+        return createModuleProperties(amFmConfig, dabTableEntries);
+    }
+
+    private static RadioManager.ModuleProperties createModuleProperties(
+            AmFmRegionConfig amFmConfig, DabTableEntry[] dabTableEntries) {
         Properties properties = createHalProperties();
 
         return ConversionUtils.propertiesFromHalProperties(TEST_ID, TEST_SERVICE_NAME, properties,
@@ -660,7 +1000,8 @@
     private static Properties createHalProperties() {
         Properties halProperties = new Properties();
         halProperties.supportedIdentifierTypes = new int[]{IdentifierType.AMFM_FREQUENCY_KHZ,
-                IdentifierType.RDS_PI, IdentifierType.DAB_SID_EXT};
+                IdentifierType.RDS_PI, IdentifierType.DAB_SID_EXT, IdentifierType.HD_STATION_ID_EXT,
+                IdentifierType.DRMO_SERVICE_ID};
         halProperties.maker = TEST_MAKER;
         halProperties.product = TEST_PRODUCT;
         halProperties.version = TEST_VERSION;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
index ce27bc1..d64fcaf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ProgramInfoCacheTest.java
@@ -440,6 +440,29 @@
                 TEST_DAB_UNIQUE_ID_ALTERNATIVE);
     }
 
+    @Test
+    public void filterAndApplyChunkInternal_withInvalidProgramInfoAndIdentifiers()
+            throws RemoteException {
+        ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null,
+                /* complete= */ false, TEST_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO);
+        ProgramInfo[] halModified = new android.hardware.broadcastradio.ProgramInfo[1];
+        halModified[0] = AidlTestUtils.makeHalProgramInfo(
+                ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR_ALTERNATIVE),
+                ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
+                ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_FREQUENCY_ID_ALTERNATIVE),
+                TEST_SIGNAL_QUALITY);
+        ProgramIdentifier[] halRemoved = new android.hardware.broadcastradio.ProgramIdentifier[1];
+        halRemoved[0] = new android.hardware.broadcastradio.ProgramIdentifier();
+        ProgramListChunk halChunk = AidlTestUtils.makeHalChunk(/* purge= */ false,
+                /* complete= */ true, halModified, halRemoved);
+
+        List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk,
+                TEST_MAX_NUM_MODIFIED_PER_CHUNK, TEST_MAX_NUM_REMOVED_PER_CHUNK);
+
+        expect.withMessage("Program list chunk applied with invalid program and identifiers")
+                .that(programListChunks).isEmpty();
+    }
+
     private void verifyChunkListPurge(List<ProgramList.Chunk> chunks, boolean purge) {
         if (chunks.isEmpty()) {
             return;
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
index 10ac05d..a952bde 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java
@@ -16,13 +16,12 @@
 
 package com.android.server.broadcastradio.aidl;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -32,9 +31,13 @@
 import android.hardware.radio.IAnnouncementListener;
 import android.hardware.radio.ICloseHandle;
 import android.hardware.radio.RadioManager;
+import android.os.ParcelableException;
 import android.os.RemoteException;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -50,6 +53,9 @@
     private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES =
             AidlTestUtils.makeDefaultModuleProperties();
 
+    @Rule
+    public final Expect mExpect = Expect.create();
+
     // Mocks
     @Mock
     private IBroadcastRadio mBroadcastRadioMock;
@@ -77,13 +83,13 @@
 
     @Test
     public void getService() {
-        assertWithMessage("Service of radio module")
+        mExpect.withMessage("Service of radio module")
                 .that(mRadioModule.getService()).isEqualTo(mBroadcastRadioMock);
     }
 
     @Test
     public void getProperties() {
-        assertWithMessage("Module properties of radio module")
+        mExpect.withMessage("Module properties of radio module")
                 .that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES);
     }
 
@@ -93,7 +99,7 @@
 
         Bitmap imageTest = mRadioModule.getImage(imageId);
 
-        assertWithMessage("Image from radio module").that(imageTest).isNull();
+        mExpect.withMessage("Image from radio module").that(imageTest).isNull();
     }
 
     @Test
@@ -104,7 +110,7 @@
             mRadioModule.getImage(invalidImageId);
         });
 
-        assertWithMessage("Exception for getting image with invalid ID")
+        mExpect.withMessage("Exception for getting image with invalid ID")
                 .that(thrown).hasMessageThat().contains("Image ID is missing");
     }
 
@@ -117,6 +123,18 @@
     }
 
     @Test
+    public void addAnnouncementListener_whenHalThrowsRemoteException() throws Exception {
+        doThrow(new RuntimeException("HAL service died")).when(mBroadcastRadioMock)
+                .registerAnnouncementListener(any(), any());
+
+        ParcelableException thrown = assertThrows(ParcelableException.class, () ->
+                mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE}));
+
+        mExpect.withMessage("Exception for adding announcement listener when HAL service died")
+                .that(thrown).hasMessageThat().contains("unknown error");
+    }
+
+    @Test
     public void onListUpdate_forAnnouncementListener() throws Exception {
         android.hardware.broadcastradio.Announcement halAnnouncement =
                 AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index 755bcdb..e963caf 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -50,6 +50,7 @@
 import android.hardware.radio.RadioTuner;
 import android.hardware.radio.UniqueProgramIdentifier;
 import android.os.Binder;
+import android.os.DeadObjectException;
 import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -308,6 +309,20 @@
     }
 
     @Test
+    public void close_forMultipleTimes() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        int errorCode = RadioTuner.ERROR_SERVER_DIED;
+        mTunerSessions[0].close(errorCode);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+
+        mTunerSessions[0].close(errorCode);
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+        expect.withMessage("Close state of broadcast radio service session for multiple times")
+                .that(mTunerSessions[0].isClosed()).isTrue();
+    }
+
+    @Test
     public void closeSessions_withMultipleSessions_withError() throws Exception {
         int numSessions = 3;
         openAidlClients(numSessions);
@@ -366,6 +381,18 @@
     }
 
     @Test
+    public void tune_withDeadTunerCallback_removesDeadSession() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        doThrow(new DeadObjectException()).when(mAidlTunerCallbackMocks[0])
+                .onCurrentProgramInfoChanged(any());
+
+        mTunerSessions[0].tune(sel);
+
+        verify(mBroadcastRadioMock, CALLBACK_TIMEOUT).unsetTunerCallback();
+    }
+
+    @Test
     public void tune_withUnsupportedSelector_throwsException() throws Exception {
         openAidlClients(/* numClients= */ 1);
 
@@ -421,6 +448,19 @@
     }
 
     @Test
+    public void tune_withClosedTuner_fails() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        mTunerSessions[0].close();
+
+        IllegalStateException thrown = assertThrows(IllegalStateException.class,
+                () -> mTunerSessions[0].tune(sel));
+
+        expect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat()
+                .contains("Tuner is closed");
+    }
+
+    @Test
     public void step_withDirectionUp() throws Exception {
         long initFreq = AM_FM_FREQUENCY_LIST[1];
         ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq);
@@ -1149,6 +1189,20 @@
     }
 
     @Test
+    public void onCurrentProgramInfoChanged_withLowerSdkVersion_doesNotInvokesCallback()
+            throws Exception {
+        doReturn(false).when(() -> CompatChanges.isChangeEnabled(
+                eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
+        openAidlClients(/* numClients= */ 1);
+
+        mHalTunerCallback.onCurrentProgramInfoChanged(
+                AidlTestUtils.programInfoToHalProgramInfo(TEST_DAB_INFO));
+
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never())
+                .onCurrentProgramInfoChanged(any());
+    }
+
+    @Test
     public void onTuneFailed_forTunerCallback() throws Exception {
         int numSessions = 3;
         openAidlClients(numSessions);
@@ -1165,6 +1219,20 @@
     }
 
     @Test
+    public void onTuneFailed_withLowerSdkVersion_doesNotInvokesCallback()
+            throws Exception {
+        doReturn(false).when(() -> CompatChanges.isChangeEnabled(
+                eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt()));
+        openAidlClients(/* numClients= */ 1);
+
+        mHalTunerCallback.onTuneFailed(Result.CANCELED,
+                ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR));
+
+        verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never())
+                .onTuneFailed(anyInt(), any());
+    }
+
+    @Test
     public void onAntennaStateChange_forTunerCallback() throws Exception {
         int numSessions = 3;
         openAidlClients(numSessions);
@@ -1231,6 +1299,36 @@
         }
     }
 
+    @Test
+    public void openSession_withNonNullAntennaState() throws Exception {
+        boolean antennaConnected = false;
+        android.hardware.radio.ITunerCallback callback =
+                mock(android.hardware.radio.ITunerCallback.class);
+        openAidlClients(/* numClients= */ 1);
+        mHalTunerCallback.onAntennaStateChange(antennaConnected);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+
+        mRadioModule.openSession(callback);
+
+        verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+    }
+
+    @Test
+    public void openSession_withNonNullCurrentProgramInfo() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        RadioManager.ProgramInfo tuneInfo = AidlTestUtils.makeProgramInfo(initialSel,
+                SIGNAL_QUALITY);
+        mTunerSessions[0].tune(initialSel);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+        android.hardware.radio.ITunerCallback callback =
+                mock(android.hardware.radio.ITunerCallback.class);
+
+        mRadioModule.openSession(callback);
+
+        verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+    }
+
     private void openAidlClients(int numClients) throws Exception {
         mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
         mTunerSessions = new TunerSession[numClients];
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
index 5e99b28..8e0abff 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java
@@ -16,11 +16,11 @@
 
 package com.android.server.broadcastradio.hal2;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -33,7 +33,10 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 
+import com.google.common.truth.Expect;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -55,6 +58,9 @@
     private AnnouncementAggregator mAnnouncementAggregator;
     private IBinder.DeathRecipient mDeathRecipient;
 
+    @Rule
+    public final Expect mExpect = Expect.create();
+
     @Mock
     private IAnnouncementListener mListenerMock;
     @Mock
@@ -76,6 +82,19 @@
     }
 
     @Test
+    public void constructor_withBinderDied() throws Exception {
+        RemoteException remoteException = new RemoteException("Binder is died");
+        doThrow(remoteException).when(mBinderMock).linkToDeath(any(), anyInt());
+
+        RuntimeException thrown = assertThrows(RuntimeException.class,
+                () -> new com.android.server.broadcastradio.aidl.AnnouncementAggregator(
+                        mListenerMock, mLock));
+
+        mExpect.withMessage("Exception for dead binder").that(thrown).hasMessageThat()
+                .contains(remoteException.getMessage());
+    }
+
+    @Test
     public void onListUpdated_withOneModuleWatcher() throws Exception {
         ArgumentCaptor<IAnnouncementListener> moduleWatcherCaptor =
                 ArgumentCaptor.forClass(IAnnouncementListener.class);
@@ -104,7 +123,7 @@
             moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index]));
 
             verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture());
-            assertWithMessage("Number of announcements %s after %s announcements were updated",
+            mExpect.withMessage("Number of announcements %s after %s announcements were updated",
                     announcementsCaptor.getValue(), index + 1)
                     .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1);
         }
@@ -132,7 +151,7 @@
                 () -> mAnnouncementAggregator.watchModule(mRadioModuleMocks[0],
                         TEST_ENABLED_TYPES));
 
-        assertWithMessage("Exception for watching module after aggregator has been closed")
+        mExpect.withMessage("Exception for watching module after aggregator has been closed")
                 .that(thrown).hasMessageThat()
                 .contains("announcement aggregator has already been closed");
     }
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
index 3de4f5d..c5c3a61 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java
@@ -16,15 +16,24 @@
 
 package com.android.server.broadcastradio.hal2;
 
+import static org.junit.Assert.assertThrows;
+
 import android.hardware.broadcastradio.V2_0.AmFmBandRange;
 import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
 import android.hardware.broadcastradio.V2_0.DabTableEntry;
 import android.hardware.broadcastradio.V2_0.IdentifierType;
+import android.hardware.broadcastradio.V2_0.Metadata;
+import android.hardware.broadcastradio.V2_0.MetadataKey;
+import android.hardware.broadcastradio.V2_0.ProgramInfo;
 import android.hardware.broadcastradio.V2_0.Properties;
+import android.hardware.broadcastradio.V2_0.Result;
 import android.hardware.broadcastradio.V2_0.VendorKeyValue;
 import android.hardware.radio.Announcement;
 import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioMetadata;
+import android.os.ParcelableException;
+import android.util.ArrayMap;
 
 import com.google.common.truth.Expect;
 
@@ -72,6 +81,24 @@
     public final Expect expect = Expect.create();
 
     @Test
+    public void throwOnError() {
+        IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+                Convert.throwOnError("tune", Result.INVALID_ARGUMENTS));
+
+        expect.withMessage("Exception for illegeal argument").that(thrown)
+                .hasMessageThat().contains("INVALID_ARGUMENTS");
+    }
+
+    @Test
+    public void throwOnError_withUnknownErrorCode() {
+        ParcelableException thrown = assertThrows(ParcelableException.class, () ->
+                Convert.throwOnError("tune", /* result= */ 1000));
+
+        expect.withMessage("Exception for unknown error code").that(thrown)
+                .hasMessageThat().contains("unknown error");
+    }
+
+    @Test
     public void propertiesFromHalProperties_idsMatch() {
         expect.withMessage("Properties id")
                 .that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID);
@@ -149,6 +176,26 @@
     }
 
     @Test
+    public void propertiesFromHalProperties_withInvalidBand() {
+        AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
+        amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(createAmFmBandRange(
+                /* lowerBound= */ 50000, /* upperBound= */ 60000, /* spacing= */ 10),
+                createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)));
+
+        RadioManager.ModuleProperties properties = convertToModuleProperties(amFmRegionConfig,
+                new ArrayList<>());
+
+        RadioManager.BandDescriptor[] bands = properties.getBands();
+        expect.withMessage("Band descriptors").that(bands).hasLength(1);
+        expect.withMessage("FM band frequency lower limit")
+                .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT);
+        expect.withMessage("FM band frequency upper limit")
+                .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT);
+        expect.withMessage("FM band frequency spacing")
+                .that(bands[0].getSpacing()).isEqualTo(FM_SPACING);
+    }
+
+    @Test
     public void announcementFromHalAnnouncement_typesMatch() {
         expect.withMessage("Announcement type")
                 .that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE);
@@ -173,20 +220,91 @@
                 .that(ANNOUNCEMENT.getVendorInfo()).isEmpty();
     }
 
+    @Test
+    public void getBands_withInvalidFrequency() {
+        expect.withMessage("Band for invalid frequency")
+                .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(FrequencyBand.UNKNOWN);
+    }
+
+    @Test
+    public void vendorInfoToHal_withNull() {
+        expect.withMessage("Null vendor info converted to HAL")
+                .that(Convert.vendorInfoToHal(/* info= */ null)).isEmpty();
+    }
+
+    @Test
+    public void vendorInfoToHal_withNullValue() {
+        Map<String, String> vendorInfo = new ArrayMap<>();
+        vendorInfo.put(VENDOR_INFO_KEY_1, null);
+
+        expect.withMessage("Vendor info with null value converted to HAL")
+                .that(Convert.vendorInfoToHal(vendorInfo)).isEmpty();
+    }
+
+    @Test
+    public void vendorInfoFromHalVendorKeyValues_withNull() {
+        expect.withMessage("Null vendor info converted from HAL")
+                .that(Convert.vendorInfoFromHal(/* info= */ null)).isEmpty();
+    }
+
+    @Test
+    public void vendorInfoFromHalVendorKeyValues_withNullElements() {
+        VendorKeyValue halVendorInfo = new VendorKeyValue();
+        halVendorInfo.key = null;
+        halVendorInfo.value = "VendorValue";
+        List<VendorKeyValue> halVendorInfoArray = List.of(halVendorInfo);
+
+        expect.withMessage("Null vendor info converted from HAL")
+                .that(Convert.vendorInfoFromHal(halVendorInfoArray)).isEmpty();
+    }
+
+    @Test
+    public void programInfoFromHal_withMetadata() {
+        int freq = 97900;
+        int signalQuality = 90;
+        String songTitle = "titleTest";
+        int albumArt = 1;
+        android.hardware.broadcastradio.V2_0.ProgramSelector halSelector =
+                TestUtils.makeHalFmSelector(freq);
+        ArrayList<Metadata> metadata = new ArrayList<>(List.of(
+                createIntMetadata(MetadataKey.ALBUM_ART, albumArt),
+                createStringMetadata(MetadataKey.SONG_TITLE, songTitle),
+                createStringMetadata(/* key= */ 1000, "valueForInvalidMetadataType")));
+        ProgramInfo halInfo = TestUtils.makeHalProgramInfo(halSelector, signalQuality,
+                /* relatedContent= */ new ArrayList<>(), metadata);
+
+        RadioManager.ProgramInfo info = Convert.programInfoFromHal(halInfo);
+
+        RadioMetadata convertedMetadata = info.getMetadata();
+        expect.withMessage("Metadata converted from HAL")
+                .that(convertedMetadata.size()).isEqualTo(2);
+        expect.withMessage("Song title")
+                .that(convertedMetadata.getString(RadioMetadata.METADATA_KEY_TITLE))
+                .isEqualTo(songTitle);
+        expect.withMessage("Album art")
+                .that(convertedMetadata.getInt(RadioMetadata.METADATA_KEY_ART))
+                .isEqualTo(albumArt);
+    }
+
     private static RadioManager.ModuleProperties convertToModuleProperties() {
         AmFmRegionConfig amFmConfig = createAmFmRegionConfig();
         List<DabTableEntry> dabTableEntries = Arrays.asList(
                 createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1),
                 createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2));
-        Properties properties = createHalProperties();
 
+        return convertToModuleProperties(amFmConfig, dabTableEntries);
+    }
+
+    private static RadioManager.ModuleProperties convertToModuleProperties(
+            AmFmRegionConfig amFmConfig, List<DabTableEntry> dabTableEntries) {
+        Properties properties = createHalProperties();
         return Convert.propertiesFromHal(TEST_ID, TEST_SERVICE_NAME, properties,
                 amFmConfig, dabTableEntries);
     }
 
     private static AmFmRegionConfig createAmFmRegionConfig() {
         AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig();
-        amFmRegionConfig.ranges = new ArrayList<AmFmBandRange>(Arrays.asList(
+        amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(
                 createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING),
                 createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING)));
         return amFmRegionConfig;
@@ -211,14 +329,29 @@
     private static Properties createHalProperties() {
         Properties halProperties = new Properties();
         halProperties.supportedIdentifierTypes = new ArrayList<Integer>(Arrays.asList(
-                IdentifierType.AMFM_FREQUENCY, IdentifierType.RDS_PI, IdentifierType.DAB_SID_EXT));
+                IdentifierType.AMFM_FREQUENCY, IdentifierType.RDS_PI, IdentifierType.DAB_SID_EXT,
+                IdentifierType.HD_STATION_ID_EXT, IdentifierType.DRMO_SERVICE_ID));
         halProperties.maker = TEST_MAKER;
         halProperties.product = TEST_PRODUCT;
         halProperties.version = TEST_VERSION;
         halProperties.serial = TEST_SERIAL;
-        halProperties.vendorInfo = new ArrayList<VendorKeyValue>(Arrays.asList(
+        halProperties.vendorInfo = new ArrayList<>(Arrays.asList(
                 TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1),
                 TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2)));
         return halProperties;
     }
+
+    private Metadata createStringMetadata(int key, String value) {
+        Metadata metadata = new Metadata();
+        metadata.key = key;
+        metadata.stringValue = value;
+        return metadata;
+    }
+
+    private Metadata createIntMetadata(int key, int value) {
+        Metadata metadata = new Metadata();
+        metadata.key = key;
+        metadata.intValue = value;
+        return metadata;
+    }
 }
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
index 36a6430..015e9c0 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java
@@ -17,6 +17,7 @@
 
 import static org.junit.Assert.*;
 
+import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
 import android.hardware.broadcastradio.V2_0.ProgramListChunk;
 import android.hardware.radio.ProgramList;
 import android.hardware.radio.ProgramSelector;
@@ -34,6 +35,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -261,6 +263,25 @@
         verifyChunkListRemoved(chunks, 1, TEST_DAB_UNIQUE_ID, TEST_VENDOR_UNIQUE_ID);
     }
 
+    @Test
+    public void filterAndApplyChunkInternal_withInvalidIdentifier() {
+        ProgramInfoCache cache = new ProgramInfoCache(/* filter= */ null, /* complete= */ false,
+                TEST_AM_FM_INFO, TEST_RDS_INFO, TEST_DAB_INFO, TEST_VENDOR_INFO);
+        ArrayList<ProgramIdentifier> halRemoved = new ArrayList<>();
+        halRemoved.add(new ProgramIdentifier());
+        ProgramListChunk halChunk = new ProgramListChunk();
+        halChunk.complete = true;
+        halChunk.purge = false;
+        halChunk.modified = new ArrayList<>();
+        halChunk.removed = halRemoved;
+
+        List<ProgramList.Chunk> programListChunks = cache.filterAndApplyChunkInternal(halChunk,
+                /* maxNumModifiedPerChunk= */ 1, /* maxNumRemovedPerChunk= */ 1);
+
+        expect.withMessage("Program list chunk applied with invalid identifier")
+                .that(programListChunks).isEmpty();
+    }
+
     // Verifies that:
     // - The first chunk's purge flag matches expectPurge.
     // - The last chunk's complete flag matches expectComplete.
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java
index 0b16141..94ae42b 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java
@@ -16,6 +16,7 @@
 package com.android.server.broadcastradio.hal2;
 
 import android.hardware.broadcastradio.V2_0.IdentifierType;
+import android.hardware.broadcastradio.V2_0.Metadata;
 import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
 import android.hardware.broadcastradio.V2_0.ProgramInfo;
 import android.hardware.broadcastradio.V2_0.VendorKeyValue;
@@ -127,13 +128,21 @@
 
     static ProgramInfo makeHalProgramInfo(
             android.hardware.broadcastradio.V2_0.ProgramSelector hwSel, int hwSignalQuality) {
+        return makeHalProgramInfo(hwSel, hwSignalQuality, /* relatedContent= */ new ArrayList<>(),
+                /* metadata= */ new ArrayList<>());
+    }
+
+    @SuppressWarnings("NonApiType")
+    static ProgramInfo makeHalProgramInfo(
+            android.hardware.broadcastradio.V2_0.ProgramSelector hwSel, int hwSignalQuality,
+            ArrayList<ProgramIdentifier> relatedContent, ArrayList<Metadata> metadata) {
         ProgramInfo hwInfo = new ProgramInfo();
         hwInfo.selector = hwSel;
         hwInfo.logicallyTunedTo = hwSel.primaryId;
         hwInfo.physicallyTunedTo = hwSel.primaryId;
         hwInfo.signalQuality = hwSignalQuality;
-        hwInfo.relatedContent = new ArrayList<>();
-        hwInfo.metadata = new ArrayList<>();
+        hwInfo.relatedContent = relatedContent;
+        hwInfo.metadata = metadata;
         return hwInfo;
     }
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index 6edfa02..55aae9d 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -29,8 +29,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
-import static com.google.common.truth.Truth.assertWithMessage;
-
 import static org.junit.Assert.assertThrows;
 
 import android.graphics.Bitmap;
@@ -47,6 +45,7 @@
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioTuner;
 import android.os.Binder;
+import android.os.DeadObjectException;
 import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -57,8 +56,11 @@
 import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
 import com.android.server.broadcastradio.RadioServiceUserController;
 
+import com.google.common.truth.Expect;
+
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -98,6 +100,9 @@
     private ProgramInfo mHalCurrentInfo;
     private TunerSession[] mTunerSessions;
 
+    @Rule
+    public final Expect mExpect = Expect.create();
+
     @Mock
     private UserHandle mUserHandleMock;
     @Mock
@@ -206,7 +211,7 @@
         openAidlClients(numSessions);
 
         for (int index = 0; index < numSessions; index++) {
-            assertWithMessage("Session of index %s close state", index)
+            mExpect.withMessage("Session of index %s close state", index)
                     .that(mTunerSessions[index].isClosed()).isFalse();
         }
     }
@@ -238,7 +243,7 @@
 
         RadioManager.BandConfig config = mTunerSessions[0].getConfiguration();
 
-        assertWithMessage("Session configuration").that(config)
+        mExpect.withMessage("Session configuration").that(config)
                 .isEqualTo(FM_BAND_CONFIG);
     }
 
@@ -248,7 +253,7 @@
 
         mTunerSessions[0].setMuted(/* mute= */ false);
 
-        assertWithMessage("Session mute state after setting unmuted")
+        mExpect.withMessage("Session mute state after setting unmuted")
                 .that(mTunerSessions[0].isMuted()).isFalse();
     }
 
@@ -258,7 +263,7 @@
 
         mTunerSessions[0].setMuted(/* mute= */ true);
 
-        assertWithMessage("Session mute state after setting muted")
+        mExpect.withMessage("Session mute state after setting muted")
                 .that(mTunerSessions[0].isMuted()).isTrue();
     }
 
@@ -268,7 +273,7 @@
 
         mTunerSessions[0].close();
 
-        assertWithMessage("Close state of broadcast radio service session")
+        mExpect.withMessage("Close state of broadcast radio service session")
                 .that(mTunerSessions[0].isClosed()).isTrue();
     }
 
@@ -282,11 +287,11 @@
 
         for (int index = 0; index < numSessions; index++) {
             if (index == closeIdx) {
-                assertWithMessage(
+                mExpect.withMessage(
                         "Close state of broadcast radio service session of index %s", index)
                         .that(mTunerSessions[index].isClosed()).isTrue();
             } else {
-                assertWithMessage(
+                mExpect.withMessage(
                         "Close state of broadcast radio service session of index %s", index)
                         .that(mTunerSessions[index].isClosed()).isFalse();
             }
@@ -301,7 +306,21 @@
         mTunerSessions[0].close(errorCode);
 
         verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
-        assertWithMessage("Close state of broadcast radio service session")
+        mExpect.withMessage("Close state of broadcast radio service session")
+                .that(mTunerSessions[0].isClosed()).isTrue();
+    }
+
+    @Test
+    public void close_forMultipleTimes() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        int errorCode = RadioTuner.ERROR_SERVER_DIED;
+        mTunerSessions[0].close(errorCode);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+
+        mTunerSessions[0].close(errorCode);
+
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode);
+        mExpect.withMessage("State of closing broadcast radio service session twice")
                 .that(mTunerSessions[0].isClosed()).isTrue();
     }
 
@@ -315,7 +334,7 @@
 
         for (int index = 0; index < numSessions; index++) {
             verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT).onError(errorCode);
-            assertWithMessage("Close state of broadcast radio service session of index %s", index)
+            mExpect.withMessage("Close state of broadcast radio service session of index %s", index)
                     .that(mTunerSessions[index].isClosed()).isTrue();
         }
     }
@@ -349,6 +368,18 @@
     }
 
     @Test
+    public void tune_withDeadTunerCallback_removesDeadSession() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramSelector sel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        doThrow(new DeadObjectException()).when(mAidlTunerCallbackMocks[0])
+                .onCurrentProgramInfoChanged(any());
+
+        mTunerSessions[0].tune(sel);
+
+        verify(mHalTunerSessionMock, CALLBACK_TIMEOUT).close();
+    }
+
+    @Test
     public void tune_withUnsupportedSelector_throwsException() throws Exception {
         ProgramSelector.Identifier dabPrimaryId =
                 new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT,
@@ -365,7 +396,7 @@
         UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class,
                 () -> mTunerSessions[0].tune(unsupportedSelector));
 
-        assertWithMessage("Exception for tuning on unsupported program selector")
+        mExpect.withMessage("Exception for tuning on unsupported program selector")
                 .that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED");
     }
 
@@ -393,11 +424,24 @@
             mTunerSessions[0].tune(sel);
         });
 
-        assertWithMessage("Unknown error HAL exception when tuning")
+        mExpect.withMessage("Unknown error HAL exception when tuning")
                 .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR));
     }
 
     @Test
+    public void tune_withClosedTuner_fails() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramSelector sel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        mTunerSessions[0].close();
+
+        IllegalStateException thrown = assertThrows(IllegalStateException.class,
+                () -> mTunerSessions[0].tune(sel));
+
+        mExpect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat()
+                .contains("Tuner is closed");
+    }
+
+    @Test
     public void step_withDirectionUp() throws Exception {
         long initFreq = AM_FM_FREQUENCY_LIST[1];
         ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq);
@@ -454,7 +498,7 @@
             mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
         });
 
-        assertWithMessage("Exception for stepping when HAL is in invalid state")
+        mExpect.withMessage("Exception for stepping when HAL is in invalid state")
                 .that(thrown).hasMessageThat().contains(Result.toString(Result.INVALID_STATE));
     }
 
@@ -533,7 +577,7 @@
             mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
         });
 
-        assertWithMessage("Internal error HAL exception when seeking")
+        mExpect.withMessage("Internal error HAL exception when seeking")
                 .that(thrown).hasMessageThat().contains(Result.toString(Result.INTERNAL_ERROR));
     }
 
@@ -566,7 +610,7 @@
             mTunerSessions[0].cancel();
         });
 
-        assertWithMessage("Exception for canceling when HAL throws remote exception")
+        mExpect.withMessage("Exception for canceling when HAL throws remote exception")
                 .that(thrown).hasMessageThat().contains(exceptionMessage);
     }
 
@@ -579,7 +623,7 @@
             mTunerSessions[0].getImage(imageId);
         });
 
-        assertWithMessage("Get image exception")
+        mExpect.withMessage("Get image exception")
                 .that(thrown).hasMessageThat().contains("Image ID is missing");
     }
 
@@ -590,7 +634,7 @@
 
         Bitmap imageTest = mTunerSessions[0].getImage(imageId);
 
-        assertWithMessage("Null image").that(imageTest).isEqualTo(null);
+        mExpect.withMessage("Null image").that(imageTest).isEqualTo(null);
     }
 
     @Test
@@ -603,7 +647,7 @@
             mTunerSessions[0].getImage(/* id= */ 1);
         });
 
-        assertWithMessage("Exception for getting image when HAL throws remote exception")
+        mExpect.withMessage("Exception for getting image when HAL throws remote exception")
                 .that(thrown).hasMessageThat().contains(exceptionMessage);
     }
 
@@ -649,7 +693,7 @@
             mTunerSessions[0].startProgramListUpdates(/* filter= */ null);
         });
 
-        assertWithMessage("Unknown error HAL exception when updating program list")
+        mExpect.withMessage("Unknown error HAL exception when updating program list")
                 .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR));
     }
 
@@ -686,7 +730,7 @@
         boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
 
         verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
-        assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
+        mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isFalse();
     }
 
     @Test
@@ -697,7 +741,7 @@
         boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag);
 
         verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any());
-        assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue();
+        mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isTrue();
     }
 
     @Test
@@ -709,7 +753,7 @@
             mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
         });
 
-        assertWithMessage("Exception for setting unsupported flag %s", flag)
+        mExpect.withMessage("Exception for setting unsupported flag %s", flag)
                 .that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED");
     }
 
@@ -755,7 +799,7 @@
             mTunerSessions[0].isConfigFlagSet(flag);
         });
 
-        assertWithMessage("Exception for checking if unsupported flag %s is set", flag)
+        mExpect.withMessage("Exception for checking if unsupported flag %s is set", flag)
                 .that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED");
     }
 
@@ -768,7 +812,7 @@
 
         boolean isSet = mTunerSessions[0].isConfigFlagSet(flag);
 
-        assertWithMessage("Config flag %s is set", flag)
+        mExpect.withMessage("Config flag %s is set", flag)
                 .that(isSet).isEqualTo(expectedConfigFlagValue);
     }
 
@@ -782,7 +826,7 @@
             mTunerSessions[0].isConfigFlagSet(flag);
         });
 
-        assertWithMessage("Exception for checking config flag when HAL throws remote exception")
+        mExpect.withMessage("Exception for checking config flag when HAL throws remote exception")
                 .that(thrown).hasMessageThat().contains("Failed to check flag");
     }
 
@@ -822,7 +866,7 @@
             mTunerSessions[0].setParameters(parametersSet);
         });
 
-        assertWithMessage("Exception for setting parameters when HAL throws remote exception")
+        mExpect.withMessage("Exception for setting parameters when HAL throws remote exception")
                 .that(thrown).hasMessageThat().contains(exceptionMessage);
     }
 
@@ -848,7 +892,7 @@
             mTunerSessions[0].getParameters(parameterKeys);
         });
 
-        assertWithMessage("Exception for getting parameters when HAL throws remote exception")
+        mExpect.withMessage("Exception for getting parameters when HAL throws remote exception")
                 .that(thrown).hasMessageThat().contains(exceptionMessage);
     }
 
@@ -894,6 +938,36 @@
         }
     }
 
+    @Test
+    public void openSession_withNonNullAntennaState() throws Exception {
+        boolean antennaConnected = false;
+        android.hardware.radio.ITunerCallback callback =
+                mock(android.hardware.radio.ITunerCallback.class);
+        openAidlClients(/* numClients= */ 1);
+        mHalTunerCallback.onAntennaStateChange(antennaConnected);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+
+        mRadioModule.openSession(callback);
+
+        verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected);
+    }
+
+    @Test
+    public void openSession_withNonNullCurrentProgramInfo() throws Exception {
+        openAidlClients(/* numClients= */ 1);
+        ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
+        RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel,
+                SIGNAL_QUALITY);
+        mTunerSessions[0].tune(initialSel);
+        verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+        android.hardware.radio.ITunerCallback callback =
+                mock(android.hardware.radio.ITunerCallback.class);
+
+        mRadioModule.openSession(callback);
+
+        verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo);
+    }
+
     private void openAidlClients(int numClients) throws Exception {
         mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients];
         mTunerSessions = new TunerSession[numClients];
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index 6e9d4db..94bde68 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -25,10 +25,11 @@
 
     <application
         android:theme="@style/Theme"
+        android:icon="@mipmap/ic_launcher"
+        android:roundIcon="@mipmap/ic_launcher_round"
         android:label="Battery Stats Viewer">
         <activity android:name=".BatteryConsumerPickerActivity"
                   android:label="Battery Stats"
-                  android:icon="@mipmap/ic_launcher"
                   android:launchMode="singleTop"
                   android:exported="true">
             <intent-filter>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_background.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 07d5da9..0000000
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="108dp"
-    android:height="108dp"
-    android:viewportWidth="108"
-    android:viewportHeight="108">
-    <path
-        android:fillColor="#3DDC84"
-        android:pathData="M0,0h108v108h-108z" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M9,0L9,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,0L19,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M29,0L29,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M39,0L39,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M49,0L49,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M59,0L59,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M69,0L69,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M79,0L79,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M89,0L89,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M99,0L99,108"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,9L108,9"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,19L108,19"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,29L108,29"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,39L108,39"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,49L108,49"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,59L108,59"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,69L108,69"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,79L108,79"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,89L108,89"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M0,99L108,99"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,29L89,29"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,39L89,39"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,49L89,49"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,59L89,59"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,69L89,69"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M19,79L89,79"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M29,19L29,89"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M39,19L39,89"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M49,19L49,89"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M59,19L59,89"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M69,19L69,89"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-    <path
-        android:fillColor="#00000000"
-        android:pathData="M79,19L79,89"
-        android:strokeWidth="0.8"
-        android:strokeColor="#33FFFFFF" />
-</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_foreground.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_foreground.xml
deleted file mode 100644
index fc0c6ab..0000000
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt"
-    android:width="108dp"
-    android:height="108dp"
-    android:viewportWidth="108"
-    android:viewportHeight="108">
-    <path
-        android:fillType="evenOdd"
-        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,
-        49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
-        android:strokeWidth="1"
-        android:strokeColor="#00000000">
-        <aapt:attr name="android:fillColor">
-            <gradient
-                android:endX="78.5885"
-                android:endY="90.9159"
-                android:startX="48.7653"
-                android:startY="61.0927"
-                android:type="linear">
-                <item
-                    android:color="#44000000"
-                    android:offset="0.0" />
-                <item
-                    android:color="#00000000"
-                    android:offset="1.0" />
-            </gradient>
-        </aapt:attr>
-    </path>
-    <path
-        android:fillColor="#FFFFFF"
-        android:fillType="nonZero"
-        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,
-        50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,
-        37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,
-        42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,
-        40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,
-        52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,
-        56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,
-        52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
-        android:strokeWidth="1"
-        android:strokeColor="#00000000" />
-</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
index f35a210..987de6b 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_consumer_picker_layout.xml
@@ -17,6 +17,7 @@
 <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/swipe_refresh"
+    android:paddingTop="?attr/actionBarSize"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
index cf50d2a..2d276a5 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/layout/battery_stats_viewer_layout.xml
@@ -17,11 +17,13 @@
 <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/swipe_refresh"
+    android:paddingTop="?attr/actionBarSize"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
     <LinearLayout
         android:orientation="vertical"
+        android:paddingTop="?attr/actionBarSize"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/ic_launcher_background"/>
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher_round.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/ic_launcher_background"/>
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml
index 6b78462..036d09b 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@drawable/ic_launcher_background" />
-    <foreground android:drawable="@drawable/ic_launcher_foreground" />
-</adaptive-icon>
+    <background android:drawable="@color/ic_launcher_background"/>
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher_round.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher_round.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-anydpi/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/ic_launcher_background"/>
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..0057985
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..085df9d
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..bcb3b7d
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..3d1cf0e
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..bfd4568
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..4cf0d43
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..ac4f693
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..cc6b763
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1f17221
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..b70e145
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..6e46bce
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..3fa346c
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..8b463f2
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..849caff
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_round.webp b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..bd6e312
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/ic_launcher_background.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..1e90e07
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="ic_launcher_background">#1A7945</color>
+</resources>
\ No newline at end of file
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
index 629d729..fa30b2c 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/styles.xml
@@ -21,6 +21,7 @@
         <item name="colorPrimary">#34a853</item>
         <item name="android:windowActionBar">true</item>
         <item name="android:windowNoTitle">false</item>
+        <item name="android:windowDrawsSystemBarBackgrounds">false</item>
     </style>
 
     <style name="LoadTestCardView" parent="Widget.MaterialComponents.CardView">
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index b64eeca..e9ad1c2 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -804,6 +804,7 @@
     }
 
     @Test
+    @Ignore // b/347089000 - Restore or delete
     public void testColors_ensureColors_colorized_producesValidPalette_white() {
         validateColorizedPaletteForColor(Color.WHITE);
     }
diff --git a/core/tests/coretests/src/android/hardware/display/OWNERS b/core/tests/coretests/src/android/hardware/display/OWNERS
index 9ca3910..3bcb31d 100644
--- a/core/tests/coretests/src/android/hardware/display/OWNERS
+++ b/core/tests/coretests/src/android/hardware/display/OWNERS
@@ -1,2 +1 @@
-michaelwr@google.com
-santoscordon@google.com
+file:/services/core/java/com/android/server/display/OWNERS
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 8b333f3..1c00734 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -1,11 +1,11 @@
 # Display
-per-file BrightnessLimit.java = michaelwr@google.com, santoscordon@google.com
+per-file BrightnessLimit.java = file:/services/core/java/com/android/server/display/OWNERS
 
 # Haptics
 per-file *Vibrat*.java = file:/services/core/java/com/android/server/vibrator/OWNERS
 
 # Power
-per-file PowerManager*.java = michaelwr@google.com, santoscordon@google.com
+per-file PowerManager*.java = file:/services/core/java/com/android/server/power/OWNERS
 
 # PerformanceHintManager
 per-file PerformanceHintManagerTest.java = file:/ADPF_OWNERS
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 1491d77..169300a 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -45,6 +45,7 @@
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.sysprop.ViewProperties;
 import android.util.DisplayMetrics;
 import android.widget.FrameLayout;
 import android.widget.ProgressBar;
@@ -101,6 +102,9 @@
     @RequiresFlagsEnabled({FLAG_VIEW_VELOCITY_API,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void frameRateChangesWhenContentMoves() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         waitForFrameRateCategoryToSettle();
         mActivityRule.runOnUiThread(() -> {
             mMovingView.offsetLeftAndRight(100);
@@ -127,6 +131,9 @@
     @Test
     @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
     public void frameBoostDisable() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mActivityRule.runOnUiThread(() -> {
             long now = SystemClock.uptimeMillis();
             MotionEvent down = MotionEvent.obtain(
@@ -155,6 +162,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void lowVelocity60() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mActivityRule.runOnUiThread(() -> {
             ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
             layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -175,6 +185,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void velocityWithChildMovement() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         FrameLayout frameLayout = new FrameLayout(mActivity);
         mActivityRule.runOnUiThread(() -> {
             ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams(
@@ -201,6 +214,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void highVelocity120() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mActivityRule.runOnUiThread(() -> {
             ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
             layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -222,6 +238,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void noVelocityUsesCategorySmall() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final CountDownLatch drawLatch1 = new CountDownLatch(1);
         mActivityRule.runOnUiThread(() -> {
             DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -259,6 +278,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void noVelocityUsesCategoryNarrowWidth() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final CountDownLatch drawLatch1 = new CountDownLatch(1);
         mActivityRule.runOnUiThread(() -> {
             DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -295,6 +317,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void noVelocityUsesCategoryNarrowHeight() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final CountDownLatch drawLatch1 = new CountDownLatch(1);
         mActivityRule.runOnUiThread(() -> {
             DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -331,6 +356,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void noVelocityUsesCategoryLargeWidth() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final CountDownLatch drawLatch1 = new CountDownLatch(1);
         mActivityRule.runOnUiThread(() -> {
             DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -367,6 +395,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void noVelocityUsesCategoryLargeHeight() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final CountDownLatch drawLatch1 = new CountDownLatch(1);
         mActivityRule.runOnUiThread(() -> {
             DisplayMetrics displayMetrics = mActivity.getResources().getDisplayMetrics();
@@ -403,6 +434,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void defaultNormal() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mActivityRule.runOnUiThread(() -> {
             View parent = (View) mMovingView.getParent();
             ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
@@ -427,6 +461,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY
     })
     public void frameRateAndCategory() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
         waitForFrameRateCategoryToSettle();
         mActivityRule.runOnUiThread(() -> {
@@ -447,6 +484,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
     })
     public void willNotDrawUsesCategory() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mActivityRule.runOnUiThread(() -> {
             mMovingView.setWillNotDraw(true);
             mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
@@ -480,6 +520,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void intermittentDoubleInvalidate() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         View parent = (View) mMovingView.getParent();
         mActivityRule.runOnUiThread(() -> {
             parent.setWillNotDraw(false);
@@ -499,12 +542,9 @@
                         toolkitFrameRateDefaultNormalReadOnly() ? FRAME_RATE_CATEGORY_NORMAL
                                 : FRAME_RATE_CATEGORY_HIGH;
             } else {
-                // intermittent
-                // Even though this is not a small View, step 3 is triggered by this flag, which
-                // brings intermittent to LOW
-                expectedCategory = toolkitFrameRateBySizeReadOnly()
-                        ? FRAME_RATE_CATEGORY_LOW
-                        : FRAME_RATE_CATEGORY_NORMAL;
+                // intermittent.
+                // The expected category is normal.
+                expectedCategory = FRAME_RATE_CATEGORY_NORMAL;
             }
             mActivityRule.runOnUiThread(() -> {
                 mMovingView.invalidate();
@@ -529,6 +569,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
     })
     public void sameFrameMotion() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
         waitForFrameRateCategoryToSettle();
 
@@ -552,6 +595,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
     })
     public void frameRateReset() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mMovingView.setRequestedFrameRate(120f);
         waitForFrameRateCategoryToSettle();
         mActivityRule.runOnUiThread(() -> mMovingView.setVisibility(View.INVISIBLE));
@@ -573,6 +619,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
     })
     public void frameRateResetWithInvalidations() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mMovingView.setRequestedFrameRate(120f);
         waitForFrameRateCategoryToSettle();
         mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
@@ -593,6 +642,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
     })
     public void testQuickTouchBoost() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mActivityRule.runOnUiThread(() -> {
             mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
             ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams();
@@ -633,6 +685,9 @@
             com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
     })
     public void idleDetected() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         waitForFrameRateCategoryToSettle();
         mActivityRule.runOnUiThread(() -> {
             mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
@@ -657,6 +712,9 @@
             com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
     })
     public void vectorDrawableFrameRate() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final ProgressBar[] progressBars = new ProgressBar[3];
         final ViewGroup[] parents = new ViewGroup[1];
         mActivityRule.runOnUiThread(() -> {
@@ -714,6 +772,9 @@
             com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
     })
     public void renderNodeAnimatorFrameRateCanceled() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
         waitForFrameRateCategoryToSettle();
 
@@ -751,6 +812,9 @@
             com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
     })
     public void renderNodeAnimatorFrameRateRemoved() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
         waitForFrameRateCategoryToSettle();
 
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 94e187a..06cb0ee 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -72,6 +72,7 @@
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
+import android.sysprop.ViewProperties;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.WindowInsets.Side;
@@ -503,6 +504,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_getDefaultValues() {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         ViewRootImpl viewRootImpl = new ViewRootImpl(sContext,
                 sContext.getDisplayNoVerify());
         assertEquals(FRAME_RATE_CATEGORY_DEFAULT,
@@ -521,6 +525,9 @@
             FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         attachViewToWindow(mView);
         mViewRootImpl = mView.getViewRootImpl();
@@ -558,6 +565,9 @@
             FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
         wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -590,6 +600,9 @@
             FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
         wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -627,6 +640,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh()
             throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
         wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -688,6 +704,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh()
             throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
         wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -723,6 +742,9 @@
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh()
             throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
         wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -758,6 +780,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         attachViewToWindow(mView);
         mViewRootImpl = mView.getViewRootImpl();
@@ -804,6 +829,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRate_aggregate() {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         attachViewToWindow(mView);
         mViewRootImpl = mView.getViewRootImpl();
@@ -876,6 +904,9 @@
             FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRate_category() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         attachViewToWindow(mView);
         sInstrumentation.waitForIdleSync();
@@ -930,6 +961,9 @@
             FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateCategory_velocityToHigh() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
         wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -973,6 +1007,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_insetsAnimation() {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
         wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
@@ -1010,6 +1047,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_frameRateBoostOnTouch() {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         attachViewToWindow(mView);
         sInstrumentation.waitForIdleSync();
@@ -1043,6 +1083,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateTimeOut() throws InterruptedException {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final long delay = 200L;
 
         mView = new View(sContext);
@@ -1082,6 +1125,9 @@
             FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_voteFrameRateOnly() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         float frameRate = 20;
         attachViewToWindow(mView);
@@ -1133,6 +1179,9 @@
             FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final long delay = 200L;
 
         mView = new View(sContext);
@@ -1175,11 +1224,8 @@
         // Infrequent update
         Thread.sleep(delay);
 
-        // Even though this is not a small View, step 3 is triggered by this flag, which
-        // brings intermittent to LOW
-        int intermittentExpected = toolkitFrameRateBySizeReadOnly()
-                ? FRAME_RATE_CATEGORY_LOW
-                : FRAME_RATE_CATEGORY_NORMAL;
+        // The expected category is normal for intermittent.
+        int intermittentExpected = FRAME_RATE_CATEGORY_NORMAL;
 
         sInstrumentation.runOnMainSync(() -> {
             mView.invalidate();
@@ -1211,6 +1257,9 @@
     @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_isFrameRatePowerSavingsBalanced() {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         attachViewToWindow(mView);
         sInstrumentation.waitForIdleSync();
@@ -1245,6 +1294,9 @@
             FLAG_TOOLKIT_FRAME_RATE_FUNCTION_ENABLING_READ_ONLY,
             FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY})
     public void votePreferredFrameRate_applyTextureViewHeuristic() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         final long delay = 30L;
 
         mView = new TextureView(sContext);
@@ -1289,6 +1341,9 @@
     @Test
     @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY)
     public void votePreferredFrameRate_velocityVotedAfterOnDraw() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
         mView = new View(sContext);
         double delta = 0.1;
         float pixelsPerSecond = 1000_000;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 5a6824b..b5c264c 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -146,10 +146,6 @@
 
     public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {}
 
-    public boolean isMagnificationSystemUIConnected() {
-        return false;
-    }
-
     public boolean setSoftKeyboardShowMode(int showMode) {
         return false;
     }
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 4a4c693..5a4561d 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -198,7 +198,7 @@
         }
 
         @Override
-        MainContentCaptureSession getMainCaptureSession() {
+        ContentCaptureSession getMainCaptureSession() {
             throw new UnsupportedOperationException("should not have been called");
         }
 
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
index 1cdcb37..b42bcee 100644
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java
@@ -433,6 +433,72 @@
         assertThat(session.mEvents).isEmpty();
     }
 
+    @Test
+    public void notifyViewAppearedBelowMaximumBufferSize() throws RemoteException {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ true);
+        MainContentCaptureSession session = createSession(options);
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+        session.onSessionStarted(0x2, null);
+        for (int i = 0; i < BUFFER_SIZE - 1; i++) {
+            View view = prepareView(session);
+            session.notifyViewAppeared(session.newViewStructure(view));
+        }
+        mTestableLooper.processAllMessages();
+
+        verify(mMockContentCaptureDirectManager, times(0))
+                .sendEvents(any(), anyInt(), any());
+        assertThat(session.mEvents).isNull();
+        assertThat(session.mEventProcessQueue).hasSize(BUFFER_SIZE - 1);
+    }
+
+    @Test
+    public void notifyViewAppearedExactAsMaximumBufferSize() throws RemoteException {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ true);
+        MainContentCaptureSession session = createSession(options);
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+        session.onSessionStarted(0x2, null);
+        for (int i = 0; i < BUFFER_SIZE; i++) {
+            View view = prepareView(session);
+            session.notifyViewAppeared(session.newViewStructure(view));
+        }
+        mTestableLooper.processAllMessages();
+
+        verify(mMockContentCaptureDirectManager, times(1))
+                .sendEvents(any(), anyInt(), any());
+        assertThat(session.mEvents).isEmpty();
+        assertThat(session.mEventProcessQueue).isEmpty();
+    }
+
+    @Test
+    public void notifyViewAppearedAboveMaximumBufferSize() throws RemoteException {
+        ContentCaptureOptions options =
+                createOptions(
+                        /* enableContentCaptureReceiver= */ true,
+                        /* enableContentProtectionReceiver= */ true);
+        MainContentCaptureSession session = createSession(options);
+        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
+
+        session.onSessionStarted(0x2, null);
+        for (int i = 0; i < BUFFER_SIZE * 2 + 1; i++) {
+            View view = prepareView(session);
+            session.notifyViewAppeared(session.newViewStructure(view));
+        }
+        mTestableLooper.processAllMessages();
+
+        verify(mMockContentCaptureDirectManager, times(2))
+                .sendEvents(any(), anyInt(), any());
+        assertThat(session.mEvents).isEmpty();
+        assertThat(session.mEventProcessQueue).hasSize(1);
+    }
+
     /** Simulates the regular content capture events sequence. */
     private void notifyContentCaptureEvents(final MainContentCaptureSession session) {
         final ArrayList<Object> events = new ArrayList<>(
@@ -489,11 +555,13 @@
     }
 
     private MainContentCaptureSession createSession(ContentCaptureManager manager) {
+        final Handler testHandler = Handler.createAsync(mTestableLooper.getLooper());
         MainContentCaptureSession session =
                 new MainContentCaptureSession(
                         sStrippedContext,
                         manager,
-                        Handler.createAsync(mTestableLooper.getLooper()),
+                        testHandler,
+                        testHandler,
                         mMockSystemServerInterface);
         session.mComponentName = COMPONENT_NAME;
         return session;
diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java
deleted file mode 100644
index 0075128..0000000
--- a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionV2Test.java
+++ /dev/null
@@ -1,596 +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.view.contentcapture;
-
-import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
-import static android.view.contentcapture.ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARED;
-import static android.view.contentcapture.ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARING;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-import android.content.ComponentName;
-import android.content.ContentCaptureOptions;
-import android.content.Context;
-import android.content.pm.ParceledListSlice;
-import android.graphics.Insets;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.autofill.AutofillId;
-import android.view.contentprotection.ContentProtectionEventProcessor;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Test for {@link MainContentCaptureSessionV2}.
- *
- * <p>Run with: {@code atest
- * FrameworksCoreTests:android.view.contentcapture.MainContentCaptureSessionV2Test}
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-@TestableLooper.RunWithLooper
-public class MainContentCaptureSessionV2Test {
-
-    private static final int BUFFER_SIZE = 100;
-
-    private static final int REASON = 123;
-
-    private static final ContentCaptureEvent EVENT =
-            new ContentCaptureEvent(/* sessionId= */ 0, TYPE_SESSION_STARTED);
-
-    private static final ComponentName COMPONENT_NAME =
-            new ComponentName("com.test.package", "TestClass");
-
-    private static final Context sContext = ApplicationProvider.getApplicationContext();
-
-    private static final ContentCaptureManager.StrippedContext sStrippedContext =
-            new ContentCaptureManager.StrippedContext(sContext);
-
-    private TestableLooper mTestableLooper;
-
-    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
-
-    @Mock private IContentCaptureManager mMockSystemServerInterface;
-
-    @Mock private ContentProtectionEventProcessor mMockContentProtectionEventProcessor;
-
-    @Mock private IContentCaptureDirectManager mMockContentCaptureDirectManager;
-
-    @Before
-    public void setup() {
-        mTestableLooper = TestableLooper.get(this);
-    }
-
-    @Test
-    public void onSessionStarted_contentProtectionEnabled_processorCreated() {
-        MainContentCaptureSessionV2 session = createSession();
-        assertThat(session.mContentProtectionEventProcessor).isNull();
-
-        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
-        mTestableLooper.processAllMessages();
-
-        assertThat(session.mContentProtectionEventProcessor).isNotNull();
-    }
-
-    @Test
-    public void onSessionStarted_contentProtectionDisabled_processorNotCreated() {
-        MainContentCaptureSessionV2 session =
-                createSession(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ false);
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
-        mTestableLooper.processAllMessages();
-
-        assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-    }
-
-    @Test
-    public void onSessionStarted_contentProtectionNoBuffer_processorNotCreated() {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        new ContentCaptureOptions.ContentProtectionOptions(
-                                /* enableReceiver= */ true,
-                                -BUFFER_SIZE,
-                                /* requiredGroups= */ List.of(List.of("a")),
-                                /* optionalGroups= */ Collections.emptyList(),
-                                /* optionalGroupsThreshold= */ 0));
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
-        mTestableLooper.processAllMessages();
-
-        assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-    }
-
-    @Test
-    public void onSessionStarted_contentProtectionNoGroups_processorNotCreated() {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        new ContentCaptureOptions.ContentProtectionOptions(
-                                /* enableReceiver= */ true,
-                                BUFFER_SIZE,
-                                /* requiredGroups= */ Collections.emptyList(),
-                                /* optionalGroups= */ Collections.emptyList(),
-                                /* optionalGroupsThreshold= */ 0));
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
-        mTestableLooper.processAllMessages();
-
-        assertThat(session.mContentProtectionEventProcessor).isNull();
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-    }
-
-    @Test
-    public void onSessionStarted_noComponentName_processorNotCreated() {
-        MainContentCaptureSessionV2 session = createSession();
-        session.mComponentName = null;
-
-        session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null);
-        mTestableLooper.processAllMessages();
-
-        assertThat(session.mContentProtectionEventProcessor).isNull();
-    }
-
-    @Test
-    public void sendEvent_contentCaptureDisabled_contentProtectionDisabled() {
-        MainContentCaptureSessionV2 session =
-                createSession(
-                        /* enableContentCaptureReceiver= */ false,
-                        /* enableContentProtectionReceiver= */ false);
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.sendEvent(EVENT);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mEvents).isNull();
-    }
-
-    @Test
-    public void sendEvent_contentCaptureDisabled_contentProtectionEnabled() {
-        MainContentCaptureSessionV2 session =
-                createSession(
-                        /* enableContentCaptureReceiver= */ false,
-                        /* enableContentProtectionReceiver= */ true);
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.sendEvent(EVENT);
-        mTestableLooper.processAllMessages();
-
-        verify(mMockContentProtectionEventProcessor).processEvent(EVENT);
-        assertThat(session.mEvents).isNull();
-    }
-
-    @Test
-    public void sendEvent_contentCaptureEnabled_contentProtectionDisabled() {
-        MainContentCaptureSessionV2 session =
-                createSession(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ false);
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.sendEvent(EVENT);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mEvents).isNotNull();
-        assertThat(session.mEvents).containsExactly(EVENT);
-    }
-
-    @Test
-    public void sendEvent_contentCaptureEnabled_contentProtectionEnabled() {
-        MainContentCaptureSessionV2 session = createSession();
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.sendEvent(EVENT);
-        mTestableLooper.processAllMessages();
-
-        verify(mMockContentProtectionEventProcessor).processEvent(EVENT);
-        assertThat(session.mEvents).isNotNull();
-        assertThat(session.mEvents).containsExactly(EVENT);
-    }
-
-    @Test
-    public void sendEvent_contentProtectionEnabled_processorNotCreated() {
-        MainContentCaptureSessionV2 session =
-                createSession(
-                        /* enableContentCaptureReceiver= */ false,
-                        /* enableContentProtectionReceiver= */ true);
-
-        session.sendEvent(EVENT);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mEvents).isNull();
-    }
-
-    @Test
-    public void flush_contentCaptureDisabled_contentProtectionDisabled() throws Exception {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ false,
-                        /* enableContentProtectionReceiver= */ false);
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-
-        session.flush(REASON);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        assertThat(session.mEvents).containsExactly(EVENT);
-    }
-
-    @Test
-    public void flush_contentCaptureDisabled_contentProtectionEnabled() {
-        MainContentCaptureSessionV2 session =
-                createSession(
-                        /* enableContentCaptureReceiver= */ false,
-                        /* enableContentProtectionReceiver= */ true);
-        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-
-        session.flush(REASON);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        assertThat(session.mEvents).containsExactly(EVENT);
-    }
-
-    @Test
-    public void flush_contentCaptureEnabled_contentProtectionDisabled() throws Exception {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ false);
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-
-        session.flush(REASON);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mEvents).isEmpty();
-        assertEventFlushedContentCapture(options);
-    }
-
-    @Test
-    public void flush_contentCaptureEnabled_contentProtectionEnabled() throws Exception {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mEvents = new ArrayList<>(Arrays.asList(EVENT));
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-
-        session.flush(REASON);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mEvents).isEmpty();
-        assertEventFlushedContentCapture(options);
-    }
-
-    @Test
-    public void destroySession() throws Exception {
-        MainContentCaptureSessionV2 session = createSession();
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.destroySession();
-        mTestableLooper.processAllMessages();
-
-        verify(mMockSystemServerInterface).finishSession(anyInt());
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mDirectServiceInterface).isNull();
-        assertThat(session.mContentProtectionEventProcessor).isNull();
-    }
-
-    @Test
-    public void resetSession() {
-        MainContentCaptureSessionV2 session = createSession();
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        session.resetSession(/* newState= */ 0);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockSystemServerInterface);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mDirectServiceInterface).isNull();
-        assertThat(session.mContentProtectionEventProcessor).isNull();
-    }
-
-    @Test
-    @SuppressWarnings("GuardedBy")
-    public void notifyContentCaptureEvents_notStarted_ContentCaptureDisabled_ProtectionDisabled() {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ false,
-                        /* enableContentProtectionReceiver= */ false);
-        MainContentCaptureSessionV2 session = createSession(options);
-
-        notifyContentCaptureEvents(session);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mEvents).isNull();
-    }
-
-    @Test
-    @SuppressWarnings("GuardedBy")
-    public void notifyContentCaptureEvents_started_ContentCaptureDisabled_ProtectionDisabled() {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ false,
-                        /* enableContentProtectionReceiver= */ false);
-        MainContentCaptureSessionV2 session = createSession(options);
-
-        session.onSessionStarted(0x2, null);
-        notifyContentCaptureEvents(session);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mEvents).isNull();
-    }
-
-    @Test
-    @SuppressWarnings("GuardedBy")
-    public void notifyContentCaptureEvents_notStarted_ContentCaptureEnabled_ProtectionEnabled() {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-
-        notifyContentCaptureEvents(session);
-        mTestableLooper.processAllMessages();
-
-        verifyZeroInteractions(mMockContentCaptureDirectManager);
-        verifyZeroInteractions(mMockContentProtectionEventProcessor);
-        assertThat(session.mEvents).isNull();
-    }
-
-    @Test
-    @SuppressWarnings("GuardedBy")
-    public void notifyContentCaptureEvents_started_ContentCaptureEnabled_ProtectionEnabled()
-            throws RemoteException {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-
-        session.onSessionStarted(0x2, null);
-        // Override the processor for interaction verification.
-        session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor;
-        notifyContentCaptureEvents(session);
-        mTestableLooper.processAllMessages();
-
-        // Force flush will happen twice.
-        verify(mMockContentCaptureDirectManager, times(1))
-                .sendEvents(any(), eq(FLUSH_REASON_VIEW_TREE_APPEARING), any());
-        verify(mMockContentCaptureDirectManager, times(1))
-                .sendEvents(any(), eq(FLUSH_REASON_VIEW_TREE_APPEARED), any());
-        // Other than the five view events, there will be two additional tree appearing events.
-        verify(mMockContentProtectionEventProcessor, times(7)).processEvent(any());
-        assertThat(session.mEvents).isEmpty();
-    }
-
-    @Test
-    public void notifyViewAppearedBelowMaximumBufferSize() throws RemoteException {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-
-        session.onSessionStarted(0x2, null);
-        for (int i = 0; i < BUFFER_SIZE - 1; i++) {
-            View view = prepareView(session);
-            session.notifyViewAppeared(session.newViewStructure(view));
-        }
-        mTestableLooper.processAllMessages();
-
-        verify(mMockContentCaptureDirectManager, times(0))
-                .sendEvents(any(), anyInt(), any());
-        assertThat(session.mEvents).isNull();
-        assertThat(session.mEventProcessQueue).hasSize(BUFFER_SIZE - 1);
-    }
-
-    @Test
-    public void notifyViewAppearedExactAsMaximumBufferSize() throws RemoteException {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-
-        session.onSessionStarted(0x2, null);
-        for (int i = 0; i < BUFFER_SIZE; i++) {
-            View view = prepareView(session);
-            session.notifyViewAppeared(session.newViewStructure(view));
-        }
-        mTestableLooper.processAllMessages();
-
-        verify(mMockContentCaptureDirectManager, times(1))
-                .sendEvents(any(), anyInt(), any());
-        assertThat(session.mEvents).isEmpty();
-        assertThat(session.mEventProcessQueue).isEmpty();
-    }
-
-    @Test
-    public void notifyViewAppearedAboveMaximumBufferSize() throws RemoteException {
-        ContentCaptureOptions options =
-                createOptions(
-                        /* enableContentCaptureReceiver= */ true,
-                        /* enableContentProtectionReceiver= */ true);
-        MainContentCaptureSessionV2 session = createSession(options);
-        session.mDirectServiceInterface = mMockContentCaptureDirectManager;
-
-        session.onSessionStarted(0x2, null);
-        for (int i = 0; i < BUFFER_SIZE * 2 + 1; i++) {
-            View view = prepareView(session);
-            session.notifyViewAppeared(session.newViewStructure(view));
-        }
-        mTestableLooper.processAllMessages();
-
-        verify(mMockContentCaptureDirectManager, times(2))
-                .sendEvents(any(), anyInt(), any());
-        assertThat(session.mEvents).isEmpty();
-        assertThat(session.mEventProcessQueue).hasSize(1);
-    }
-
-    /** Simulates the regular content capture events sequence. */
-    private void notifyContentCaptureEvents(final MainContentCaptureSessionV2 session) {
-        final ArrayList<Object> events = new ArrayList<>(
-                List.of(
-                        prepareView(session),
-                        prepareView(session),
-                        new AutofillId(0),
-                        prepareView(session),
-                        Insets.of(0, 0, 0, 0)
-                )
-        );
-
-        final SparseArray<ArrayList<Object>> contentCaptureEvents = new SparseArray<>();
-        contentCaptureEvents.set(session.getId(), events);
-
-        session.notifyContentCaptureEvents(contentCaptureEvents);
-    }
-
-    private View prepareView(final MainContentCaptureSessionV2 session) {
-        final View view = new View(sContext);
-        view.setContentCaptureSession(session);
-        return view;
-    }
-
-    private static ContentCaptureOptions createOptions(
-            boolean enableContentCaptureReceiver,
-            ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) {
-        return new ContentCaptureOptions(
-                /* loggingLevel= */ 0,
-                BUFFER_SIZE,
-                /* idleFlushingFrequencyMs= */ 0,
-                /* textChangeFlushingFrequencyMs= */ 0,
-                /* logHistorySize= */ 0,
-                /* disableFlushForViewTreeAppearing= */ false,
-                enableContentCaptureReceiver,
-                contentProtectionOptions,
-                /* whitelistedComponents= */ null);
-    }
-
-    private static ContentCaptureOptions createOptions(
-            boolean enableContentCaptureReceiver, boolean enableContentProtectionReceiver) {
-        return createOptions(
-                enableContentCaptureReceiver,
-                new ContentCaptureOptions.ContentProtectionOptions(
-                        enableContentProtectionReceiver,
-                        BUFFER_SIZE,
-                        /* requiredGroups= */ List.of(List.of("a")),
-                        /* optionalGroups= */ Collections.emptyList(),
-                        /* optionalGroupsThreshold= */ 0));
-    }
-
-    private ContentCaptureManager createManager(ContentCaptureOptions options) {
-        return new ContentCaptureManager(sContext, mMockSystemServerInterface, options);
-    }
-
-    private MainContentCaptureSessionV2 createSession(ContentCaptureManager manager) {
-        final Handler testHandler = Handler.createAsync(mTestableLooper.getLooper());
-        MainContentCaptureSessionV2 session =
-                new MainContentCaptureSessionV2(
-                        sStrippedContext,
-                        manager,
-                        testHandler,
-                        testHandler,
-                        mMockSystemServerInterface);
-        session.mComponentName = COMPONENT_NAME;
-        return session;
-    }
-
-    private MainContentCaptureSessionV2 createSession(ContentCaptureOptions options) {
-        return createSession(createManager(options));
-    }
-
-    private MainContentCaptureSessionV2 createSession(
-            boolean enableContentCaptureReceiver, boolean enableContentProtectionReceiver) {
-        return createSession(
-                createOptions(enableContentCaptureReceiver, enableContentProtectionReceiver));
-    }
-
-    private MainContentCaptureSessionV2 createSession() {
-        return createSession(
-                /* enableContentCaptureReceiver= */ true,
-                /* enableContentProtectionReceiver= */ true);
-    }
-
-    private void assertEventFlushedContentCapture(ContentCaptureOptions options) throws Exception {
-        ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class);
-        verify(mMockContentCaptureDirectManager)
-                .sendEvents(captor.capture(), eq(REASON), eq(options));
-
-        assertThat(captor.getValue()).isNotNull();
-        List<ContentCaptureEvent> actual = captor.getValue().getList();
-        assertThat(actual).isNotNull();
-        assertThat(actual).containsExactly(EVENT);
-    }
-}
diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
index 15c9047..c8ea374 100644
--- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java
+++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java
@@ -34,6 +34,9 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -834,33 +837,6 @@
     }
 
     @Test
-    public void visitUris_intents() {
-        RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
-
-        Uri fillIntentUri = Uri.parse("content://intent/fill");
-        views.setOnCheckedChangeResponse(
-                R.id.layout,
-                RemoteViews.RemoteResponse.fromFillInIntent(new Intent("action", fillIntentUri)));
-
-        Uri pendingIntentUri = Uri.parse("content://intent/pending");
-        PendingIntent pendingIntent = getPendingIntentWithUri(pendingIntentUri);
-        views.setOnClickResponse(
-                R.id.layout,
-                RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent));
-
-        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
-        views.visitUris(visitor);
-        verify(visitor, times(1)).accept(eq(fillIntentUri));
-        verify(visitor, times(1)).accept(eq(pendingIntentUri));
-    }
-
-    private PendingIntent getPendingIntentWithUri(Uri uri) {
-        return PendingIntent.getActivity(mContext, 0,
-                new Intent("action", uri),
-                PendingIntent.FLAG_IMMUTABLE);
-    }
-
-    @Test
     public void layoutInflaterFactory_nothingSet_returnsNull() {
         final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test);
         assertNull(rv.getLayoutInflaterFactory());
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 50d7f59..d4482f2 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -111,7 +112,7 @@
         doReturn(mApplicationInfo).when(mContext).getApplicationInfo();
 
         mDispatcher = new WindowOnBackInvokedDispatcher(mContext, Looper.getMainLooper());
-        mDispatcher.attachToWindow(mWindowSession, mWindow, mImeBackAnimationController);
+        mDispatcher.attachToWindow(mWindowSession, mWindow, null, mImeBackAnimationController);
     }
 
     private void waitForIdle() {
@@ -368,7 +369,7 @@
         callbackInfo.getCallback().onBackInvoked();
 
         waitForIdle();
-        verify(mCallback1).onBackInvoked();
+        verify(mCallback1, timeout(/*millis*/ 1000)).onBackInvoked();
         verify(mCallback1, never()).onBackCancelled();
     }
 
@@ -454,25 +455,26 @@
 
     @Test
     public void registerImeCallbacks_onBackInvokedCallbackEnabled() throws RemoteException {
-        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback);
+        verifyImeCallackRegistrations();
+    }
+
+    @Test
+    public void registerImeCallbacks_onBackInvokedCallbackDisabled() throws RemoteException {
+        doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled();
+        verifyImeCallackRegistrations();
+    }
+
+    private void verifyImeCallackRegistrations() throws RemoteException {
+        // verify default callback is replaced with ImeBackAnimationController
+        mDispatcher.registerOnBackInvokedCallbackUnchecked(mDefaultImeCallback, PRIORITY_DEFAULT);
         assertCallbacksSize(/* default */ 1, /* overlay */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mImeBackAnimationController);
 
-        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback);
+        // verify regular ime callback is successfully registered
+        mDispatcher.registerOnBackInvokedCallbackUnchecked(mImeCallback, PRIORITY_DEFAULT);
         assertCallbacksSize(/* default */ 2, /* overlay */ 0);
         assertSetCallbackInfo();
         assertTopCallback(mImeCallback);
     }
-
-    @Test
-    public void registerImeCallbacks_legacyBack() throws RemoteException {
-        doReturn(false).when(mApplicationInfo).isOnBackInvokedCallbackEnabled();
-
-        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mDefaultImeCallback);
-        assertNoSetCallbackInfo();
-
-        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mImeCallback);
-        assertNoSetCallbackInfo();
-    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index f79ba28..af2a2bb 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -48,7 +48,8 @@
         final String dumyIconKey = "dummyIcon1";
         final ArrayMap<String, StatusBarIcon> iconMap = new ArrayMap<>();
         iconMap.put(dumyIconKey, new StatusBarIcon("com.android.internal.statusbar.test",
-                UserHandle.of(100), 123, 1, 2, "dummyIconDescription"));
+                UserHandle.of(100), 123, 1, 2, "dummyIconDescription",
+                StatusBarIcon.Type.SystemIcon));
         final LetterboxDetails letterboxDetails = new LetterboxDetails(
                 /* letterboxInnerBounds= */ new Rect(1, 2, 3, 4),
                 /* letterboxFullBounds= */ new Rect(5, 6, 7, 8),
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
index fe552a0..a895378 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
@@ -44,7 +44,8 @@
         final int dummyIconNumber = 2;
         final CharSequence dummyIconContentDescription = "dummyIcon";
         final StatusBarIcon original = new StatusBarIcon(dummyIconPackageName, dummyUserHandle,
-                dummyIconId, dummyIconLevel, dummyIconNumber, dummyIconContentDescription);
+                dummyIconId, dummyIconLevel, dummyIconNumber, dummyIconContentDescription,
+                StatusBarIcon.Type.SystemIcon);
 
         final StatusBarIcon copy = clone(original);
 
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java
old mode 100755
new mode 100644
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiPortInfoTest.java
old mode 100755
new mode 100644
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 82d2381..5d4139e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -23,23 +23,19 @@
     <!-- Needed for Build.getSerial(), which is used to send a unique number for serial, per HUIG. -->
     <privapp-permissions package="android.car.usb.handler">
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.angle">
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.apps.tag">
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.backupconfirm">
         <permission name="android.permission.BACKUP"/>
         <permission name="android.permission.CRYPT_KEEPER"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.credentialmanager">
@@ -50,13 +46,11 @@
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.imsserviceentitlement">
         <permission name="android.permission.MODIFY_PHONE_STATE" />
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.launcher3">
@@ -68,7 +62,6 @@
         <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
         <permission name="android.permission.UPDATE_DEVICE_STATS"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.managedprovisioning">
@@ -98,7 +91,6 @@
         <permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
         <permission name="android.permission.BIND_CARRIER_SERVICES"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.mtp">
@@ -108,19 +100,16 @@
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.musicfx">
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.networkrecommendation">
         <permission name="android.permission.SCORE_NETWORKS"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.packageinstaller">
@@ -201,7 +190,6 @@
         <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.LOG_COMPAT_CHANGE" />
         <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.contacts">
@@ -215,7 +203,6 @@
         <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
         <permission name="android.permission.LOG_COMPAT_CHANGE" />
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.downloads">
@@ -228,7 +215,6 @@
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.UPDATE_DEVICE_STATS"/>
         <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.telephony">
@@ -238,7 +224,6 @@
         <!-- Permissions required for reading and logging compat changes -->
         <permission name="android.permission.LOG_COMPAT_CHANGE" />
         <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.server.telecom">
@@ -254,13 +239,11 @@
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
         <permission name="android.permission.STOP_APP_SWITCHES"/>
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.sharedstoragebackup">
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.shell">
@@ -602,12 +585,10 @@
         <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
         <permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.soundpicker">
         <permission name="android.permission.INTERACT_ACROSS_USERS" />
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.tv">
@@ -619,18 +600,15 @@
         <permission name="android.permission.READ_CONTENT_RATING_SYSTEMS"/>
         <permission name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"/>
         <permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.vpndialogs">
         <permission name="android.permission.CONTROL_VPN"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.wallpaper.livepicker">
         <permission name="android.permission.SET_WALLPAPER_COMPONENT"/>
         <permission name="android.permission.BIND_WALLPAPER"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.wallpaper">
@@ -638,15 +616,14 @@
         <permission name="android.permission.BIND_WALLPAPER"/>
         <permission name="android.permission.CUSTOMIZE_SYSTEM_UI"/>
         <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.dynsystem">
         <permission name="android.permission.REBOOT"/>
         <permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
         <permission name="android.permission.READ_OEM_UNLOCK_STATE"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
+
     <privapp-permissions package="com.android.settings">
         <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
         <permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
@@ -657,12 +634,10 @@
 
     <privapp-permissions package="com.android.bips">
         <permission name="android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.calllogbackup">
         <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
-        <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
     </privapp-permissions>
 
    <privapp-permissions package="com.android.devicediagnostics">
diff --git a/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Awaken_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Bounce_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Drip_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Gallop_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Nudge_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Orbit_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Rise_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Sway_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg b/data/sounds/alarms/material/ogg/Wag_OG7_1ch_48k.ogg
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/downloads/training/LocationUpdates.zip b/docs/downloads/training/LocationUpdates.zip
old mode 100755
new mode 100644
Binary files differ
diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig
index 0b9e72d..5ad97e6 100644
--- a/graphics/java/android/framework_graphics.aconfig
+++ b/graphics/java/android/framework_graphics.aconfig
@@ -24,3 +24,12 @@
     description: "Return null when decode from URI fails in Icon.loadDrawable()"
     bug: "335878768"
 }
+
+flag {
+  name: "ok_lab_colorspace"
+  is_exported: true
+  is_fixed_read_only: true
+  namespace: "core_graphics"
+  description: "Add OkLab ColorSpace support"
+  bug: "344038816"
+}
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 4bc3ece..3752257 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.AnyThread;
+import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,9 +28,13 @@
 import android.hardware.DataSpace.ColorDataSpace;
 import android.util.SparseIntArray;
 
+import com.android.graphics.flags.Flags;
+
 import libcore.util.NativeAllocationRegistry;
 
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.function.DoubleUnaryOperator;
 
 /**
@@ -230,7 +235,9 @@
                     -2392 / 128.0, 8192 / 1305.0, Rgb.TransferParameters.TYPE_PQish);
 
     // See static initialization block next to #get(Named)
-    private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length];
+    private static final HashMap<Integer, ColorSpace> sNamedColorSpaceMap =
+            new HashMap<>();
+
     private static final SparseIntArray sDataToColorSpaces = new SparseIntArray();
 
     @NonNull private final String mName;
@@ -745,7 +752,23 @@
          *     <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr>
          * </table>
          */
-        BT2020_PQ
+        BT2020_PQ,
+
+        /**
+         * <p>{@link ColorSpace.Lab Lab} color space OkLab standardized as
+         * OkLab</p>
+         * <table summary="Color space definition">
+         *     <tr><th>Property</th><th colspan="4">Value</th></tr>
+         *     <tr><td>Name</td><td colspan="4">Oklab</td></tr>
+         *     <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr>
+         *     <tr>
+         *         <td>Range</td>
+         *         <td colspan="4">\(L: `[0.0, 1.0]`, a: `[-2, 2]`, b: `[-2, 2]`\)</td>
+         *     </tr>
+         * </table>
+         */
+        @FlaggedApi(Flags.FLAG_OK_LAB_COLORSPACE)
+        OK_LAB
         // Update the initialization block next to #get(Named) when adding new values
     }
 
@@ -1428,11 +1451,11 @@
      */
     @NonNull
     static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
-        if (index < 0 || index >= sNamedColorSpaces.length) {
-            throw new IllegalArgumentException("Invalid ID, must be in the range [0.." +
-                    sNamedColorSpaces.length + ")");
+        ColorSpace colorspace = sNamedColorSpaceMap.get(index);
+        if (colorspace == null) {
+            throw new IllegalArgumentException("Invalid ID: " + index);
         }
-        return sNamedColorSpaces[index];
+        return colorspace;
     }
 
     /**
@@ -1485,12 +1508,20 @@
      *
      * <p>This method is thread-safe.</p>
      *
+     * Note that in the Android W release and later, this can return the SRGB ColorSpace if
+     * the {@link ColorSpace.Named} parameter is not enabled in the corresponding release.
+     *
      * @param name The name of the color space to get an instance of
-     * @return A non-null {@link ColorSpace} instance
+     * @return A non-null {@link ColorSpace} instance. If the ColorSpace is not supported
+     * then the SRGB ColorSpace is returned.
      */
     @NonNull
     public static ColorSpace get(@NonNull Named name) {
-        return sNamedColorSpaces[name.ordinal()];
+        ColorSpace colorSpace = sNamedColorSpaceMap.get(name.ordinal());
+        if (colorSpace == null) {
+            return sNamedColorSpaceMap.get(Named.SRGB.ordinal());
+        }
+        return colorSpace;
     }
 
     /**
@@ -1511,7 +1542,8 @@
             @NonNull @Size(9) float[] toXYZD50,
             @NonNull Rgb.TransferParameters function) {
 
-        for (ColorSpace colorSpace : sNamedColorSpaces) {
+        Collection<ColorSpace> colorspaces = sNamedColorSpaceMap.values();
+        for (ColorSpace colorSpace : colorspaces) {
             if (colorSpace.getModel() == Model.RGB) {
                 ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ);
                 if (compare(toXYZD50, rgb.mTransform) &&
@@ -1525,25 +1557,25 @@
     }
 
     static {
-        sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.SRGB.ordinal(), new ColorSpace.Rgb(
                 "sRGB IEC61966-2.1",
                 SRGB_PRIMARIES,
                 ILLUMINANT_D65,
                 null,
                 SRGB_TRANSFER_PARAMETERS,
                 Named.SRGB.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB, Named.SRGB.ordinal());
-        sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.LINEAR_SRGB.ordinal(), new ColorSpace.Rgb(
                 "sRGB IEC61966-2.1 (Linear)",
                 SRGB_PRIMARIES,
                 ILLUMINANT_D65,
                 1.0,
                 0.0f, 1.0f,
                 Named.LINEAR_SRGB.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB_LINEAR, Named.LINEAR_SRGB.ordinal());
-        sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb(
                 "scRGB-nl IEC 61966-2-2:2003",
                 SRGB_PRIMARIES,
                 ILLUMINANT_D65,
@@ -1553,112 +1585,113 @@
                 -0.799f, 2.399f,
                 SRGB_TRANSFER_PARAMETERS,
                 Named.EXTENDED_SRGB.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_SCRGB, Named.EXTENDED_SRGB.ordinal());
-        sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.LINEAR_EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb(
                 "scRGB IEC 61966-2-2:2003",
                 SRGB_PRIMARIES,
                 ILLUMINANT_D65,
                 1.0,
                 -0.5f, 7.499f,
                 Named.LINEAR_EXTENDED_SRGB.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(
                 DataSpace.DATASPACE_SCRGB_LINEAR, Named.LINEAR_EXTENDED_SRGB.ordinal());
-        sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.BT709.ordinal(), new ColorSpace.Rgb(
                 "Rec. ITU-R BT.709-5",
                 SRGB_PRIMARIES,
                 ILLUMINANT_D65,
                 null,
                 SMPTE_170M_TRANSFER_PARAMETERS,
                 Named.BT709.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_BT709, Named.BT709.ordinal());
-        sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.BT2020.ordinal(), new ColorSpace.Rgb(
                 "Rec. ITU-R BT.2020-1",
                 BT2020_PRIMARIES,
                 ILLUMINANT_D65,
                 null,
                 new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45),
                 Named.BT2020.ordinal()
-        );
+        ));
+
         sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020, Named.BT2020.ordinal());
-        sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.DCI_P3.ordinal(), new ColorSpace.Rgb(
                 "SMPTE RP 431-2-2007 DCI (P3)",
                 DCI_P3_PRIMARIES,
                 new float[] { 0.314f, 0.351f },
                 2.6,
                 0.0f, 1.0f,
                 Named.DCI_P3.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_DCI_P3, Named.DCI_P3.ordinal());
-        sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.DISPLAY_P3.ordinal(), new ColorSpace.Rgb(
                 "Display P3",
                 DCI_P3_PRIMARIES,
                 ILLUMINANT_D65,
                 null,
                 SRGB_TRANSFER_PARAMETERS,
                 Named.DISPLAY_P3.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_P3, Named.DISPLAY_P3.ordinal());
-        sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.NTSC_1953.ordinal(), new ColorSpace.Rgb(
                 "NTSC (1953)",
                 NTSC_1953_PRIMARIES,
                 ILLUMINANT_C,
                 null,
                 SMPTE_170M_TRANSFER_PARAMETERS,
                 Named.NTSC_1953.ordinal()
-        );
-        sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb(
+        ));
+        sNamedColorSpaceMap.put(Named.SMPTE_C.ordinal(), new ColorSpace.Rgb(
                 "SMPTE-C RGB",
                 new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f },
                 ILLUMINANT_D65,
                 null,
                 SMPTE_170M_TRANSFER_PARAMETERS,
                 Named.SMPTE_C.ordinal()
-        );
-        sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb(
+        ));
+        sNamedColorSpaceMap.put(Named.ADOBE_RGB.ordinal(), new ColorSpace.Rgb(
                 "Adobe RGB (1998)",
                 new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f },
                 ILLUMINANT_D65,
                 2.2,
                 0.0f, 1.0f,
                 Named.ADOBE_RGB.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_ADOBE_RGB, Named.ADOBE_RGB.ordinal());
-        sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.PRO_PHOTO_RGB.ordinal(), new ColorSpace.Rgb(
                 "ROMM RGB ISO 22028-2:2013",
                 new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f },
                 ILLUMINANT_D50,
                 null,
                 new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8),
                 Named.PRO_PHOTO_RGB.ordinal()
-        );
-        sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb(
+        ));
+        sNamedColorSpaceMap.put(Named.ACES.ordinal(), new ColorSpace.Rgb(
                 "SMPTE ST 2065-1:2012 ACES",
                 new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f },
                 ILLUMINANT_D60,
                 1.0,
                 -65504.0f, 65504.0f,
                 Named.ACES.ordinal()
-        );
-        sNamedColorSpaces[Named.ACESCG.ordinal()] = new ColorSpace.Rgb(
+        ));
+        sNamedColorSpaceMap.put(Named.ACESCG.ordinal(), new ColorSpace.Rgb(
                 "Academy S-2014-004 ACEScg",
                 new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f },
                 ILLUMINANT_D60,
                 1.0,
                 -65504.0f, 65504.0f,
                 Named.ACESCG.ordinal()
-        );
-        sNamedColorSpaces[Named.CIE_XYZ.ordinal()] = new Xyz(
+        ));
+        sNamedColorSpaceMap.put(Named.CIE_XYZ.ordinal(), new Xyz(
                 "Generic XYZ",
                 Named.CIE_XYZ.ordinal()
-        );
-        sNamedColorSpaces[Named.CIE_LAB.ordinal()] = new ColorSpace.Lab(
+        ));
+        sNamedColorSpaceMap.put(Named.CIE_LAB.ordinal(), new ColorSpace.Lab(
                 "Generic L*a*b*",
                 Named.CIE_LAB.ordinal()
-        );
-        sNamedColorSpaces[Named.BT2020_HLG.ordinal()] = new ColorSpace.Rgb(
+        ));
+        sNamedColorSpaceMap.put(Named.BT2020_HLG.ordinal(), new ColorSpace.Rgb(
                 "Hybrid Log Gamma encoding",
                 BT2020_PRIMARIES,
                 ILLUMINANT_D65,
@@ -1668,9 +1701,9 @@
                 0.0f, 1.0f,
                 BT2020_HLG_TRANSFER_PARAMETERS,
                 Named.BT2020_HLG.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_HLG, Named.BT2020_HLG.ordinal());
-        sNamedColorSpaces[Named.BT2020_PQ.ordinal()] = new ColorSpace.Rgb(
+        sNamedColorSpaceMap.put(Named.BT2020_PQ.ordinal(), new ColorSpace.Rgb(
                 "Perceptual Quantizer encoding",
                 BT2020_PRIMARIES,
                 ILLUMINANT_D65,
@@ -1680,8 +1713,14 @@
                 0.0f, 1.0f,
                 BT2020_PQ_TRANSFER_PARAMETERS,
                 Named.BT2020_PQ.ordinal()
-        );
+        ));
         sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_PQ, Named.BT2020_PQ.ordinal());
+        if (Flags.okLabColorspace()) {
+            sNamedColorSpaceMap.put(Named.OK_LAB.ordinal(), new ColorSpace.OkLab(
+                    "Oklab",
+                    Named.OK_LAB.ordinal()
+            ));
+        }
     }
 
     private static double transferHLGOETF(Rgb.TransferParameters params, double x) {
@@ -2142,10 +2181,103 @@
 
             return v;
         }
+    }
 
-        private static float clamp(float x, float min, float max) {
-            return x < min ? min : x > max ? max : x;
+    private static float clamp(float x, float min, float max) {
+        return x < min ? min : x > max ? max : x;
+    }
+
+    /**
+     * Implementation of the Oklab color space. Oklab uses a D65 white point.
+     */
+    @AnyThread
+    private static final class OkLab extends ColorSpace {
+
+        private OkLab(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) {
+            super(name, Model.LAB, id);
         }
+
+        @Override
+        public boolean isWideGamut() {
+            return true;
+        }
+
+        @Override
+        public float getMinValue(@IntRange(from = 0, to = 3) int component) {
+            return component == 0 ? 0.0f : -0.5f;
+        }
+
+        @Override
+        public float getMaxValue(@IntRange(from = 0, to = 3) int component) {
+            return component == 0 ? 1.0f : 0.5f;
+        }
+
+        @Override
+        public float[] toXyz(@NonNull @Size(min = 3) float[] v) {
+            v[0] = clamp(v[0], 0.0f, 1.0f);
+            v[1] = clamp(v[1], -0.5f, 0.5f);
+            v[2] = clamp(v[2], -0.5f, 0.5f);
+
+            mul3x3Float3(INVERSE_M2, v);
+            v[0] = v[0] * v[0] * v[0];
+            v[1] = v[1] * v[1] * v[1];
+            v[2] = v[2] * v[2] * v[2];
+
+            mul3x3Float3(INVERSE_M1, v);
+
+            return v;
+        }
+
+        @Override
+        public float[] fromXyz(@NonNull @Size(min = 3) float[] v) {
+            mul3x3Float3(M1, v);
+
+            v[0] = (float) Math.cbrt(v[0]);
+            v[1] = (float) Math.cbrt(v[1]);
+            v[2] = (float) Math.cbrt(v[2]);
+
+            mul3x3Float3(M2, v);
+            return v;
+        }
+
+        /**
+         * Temp array used as input to compute M1 below
+         */
+        private static final float[] M1TMP = {
+                0.8189330101f, 0.0329845436f, 0.0482003018f,
+                0.3618667424f, 0.9293118715f, 0.2643662691f,
+                -0.1288597137f, 0.0361456387f, 0.6338517070f
+        };
+
+        /**
+         * This is the matrix applied before the nonlinear transform for (D50) XYZ-to-Oklab.
+         * This combines the D50-to-D65 white point transform with the normal transform matrix
+         * because this is always done together in [fromXyz].
+         */
+        private static final float[] M1 = mul3x3(
+                M1TMP,
+                chromaticAdaptation(Adaptation.BRADFORD, ILLUMINANT_D50, ILLUMINANT_D65)
+        );
+
+        /**
+         * Matrix applied after the nonlinear transform.
+         */
+        private static final float[] M2 = {
+                0.2104542553f, 1.9779984951f, 0.0259040371f,
+                0.7936177850f, -2.4285922050f, 0.7827717662f,
+                -0.0040720468f, 0.4505937099f, -0.8086757660f
+        };
+
+        /**
+         * The inverse of the [M1] matrix, transforming back to XYZ (D50)
+         */
+        private static final float[] INVERSE_M1 = inverse3x3(M1);
+
+        /**
+         * The inverse of the [M2] matrix, doing the first linear transform in the
+         * Oklab-to-XYZ before doing the nonlinear transform.
+         */
+        private static final float[] INVERSE_M2 = inverse3x3(M2);
     }
 
     /**
diff --git a/keystore/OWNERS b/keystore/OWNERS
index 913f655..6891777 100644
--- a/keystore/OWNERS
+++ b/keystore/OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 189335
+drysdale@google.com
 eranm@google.com
 jbires@google.com
 swillden@google.com
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 5825fac..eea5690 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -679,6 +679,9 @@
         sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
                 new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
                         ERROR_DEVICE_REQUIRES_UPGRADE_FOR_ATTESTATION));
+        sErrorCodeToFailureInfo.put(ResponseCode.GET_ATTESTATION_APPLICATION_ID_FAILED,
+                new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+                        ERROR_INTERNAL_SYSTEM_ERROR));
         sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
                 new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
                         ERROR_ATTESTATION_KEYS_UNAVAILABLE));
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 16c77d0..ecf4720 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -24,6 +24,7 @@
 import android.app.compat.CompatChanges;
 import android.content.Context;
 import android.hardware.devicestate.DeviceStateManager;
+import android.os.SystemProperties;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -50,6 +51,11 @@
     private static final String TAG = "WindowExtensionsImpl";
 
     /**
+     * The value of the system property that indicates no override is set.
+     */
+    private static final int NO_LEVEL_OVERRIDE = -1;
+
+    /**
      * The min version of the WM Extensions that must be supported in the current platform version.
      */
     @VisibleForTesting
@@ -66,14 +72,30 @@
 
     WindowExtensionsImpl() {
         mIsActivityEmbeddingEnabled = isActivityEmbeddingEnabled();
-        Log.i(TAG, "Initializing Window Extensions, vendor API level=" + mVersion
-                + ", activity embedding enabled=" + mIsActivityEmbeddingEnabled);
+
+        Log.i(TAG, generateLogMessage());
+    }
+
+    private String generateLogMessage() {
+        final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, "
+                + "vendor API level=" + mVersion);
+        final int levelOverride = getLevelOverride();
+        if (levelOverride != NO_LEVEL_OVERRIDE) {
+            logBuilder.append(", override to ").append(levelOverride);
+        }
+        logBuilder.append(", activity embedding enabled=").append(mIsActivityEmbeddingEnabled);
+        return logBuilder.toString();
     }
 
     // TODO(b/241126279) Introduce constants to better version functionality
     @Override
     public int getVendorApiLevel() {
-        return mVersion;
+        final int levelOverride = getLevelOverride();
+        return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion;
+    }
+
+    private int getLevelOverride() {
+        return SystemProperties.getInt("persist.wm.debug.ext_version_override", NO_LEVEL_OVERRIDE);
     }
 
     @NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index e8f01c2..290fefa 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -16,6 +16,9 @@
 
 package androidx.window.extensions.embedding;
 
+import static android.content.pm.ActivityInfo.CONFIG_DENSITY;
+import static android.content.pm.ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -40,7 +43,6 @@
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
@@ -683,46 +685,53 @@
                 ? taskBounds.width() - mProperties.mDividerWidthPx
                 : taskBounds.height() - mProperties.mDividerWidthPx;
 
-        if (isDraggingToFullscreenAllowed(mProperties.mDividerAttributes)) {
-            final float displayDensity = getDisplayDensity();
-            return dividerPositionWithDraggingToFullscreenAllowed(
-                    dividerPosition,
-                    minPosition,
-                    maxPosition,
-                    fullyExpandedPosition,
-                    velocity,
-                    displayDensity);
-        }
-        return Math.clamp(dividerPosition, minPosition, maxPosition);
+        final float displayDensity = getDisplayDensity();
+        final boolean isDraggingToFullscreenAllowed =
+                isDraggingToFullscreenAllowed(mProperties.mDividerAttributes);
+        return dividerPositionWithPositionOptions(
+                dividerPosition,
+                minPosition,
+                maxPosition,
+                fullyExpandedPosition,
+                velocity,
+                displayDensity,
+                isDraggingToFullscreenAllowed);
     }
 
     /**
-     * Returns the divider position given a set of position options. A snap algorithm is used to
-     * adjust the ending position to either fully expand one container or move the divider back to
-     * the specified min/max ratio depending on the dragging velocity.
+     * Returns the divider position given a set of position options. A snap algorithm can adjust
+     * the ending position to either fully expand one container or move the divider back to
+     * the specified min/max ratio depending on the dragging velocity and if dragging to fullscreen
+     * is allowed.
      */
     @VisibleForTesting
-    static int dividerPositionWithDraggingToFullscreenAllowed(int dividerPosition, int minPosition,
-            int maxPosition, int fullyExpandedPosition, float velocity, float displayDensity) {
-        final float minDismissVelocityPxPerSecond =
-                MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity;
+    static int dividerPositionWithPositionOptions(int dividerPosition, int minPosition,
+            int maxPosition, int fullyExpandedPosition, float velocity, float displayDensity,
+            boolean isDraggingToFullscreenAllowed) {
+        if (isDraggingToFullscreenAllowed) {
+            final float minDismissVelocityPxPerSecond =
+                    MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity;
+            if (dividerPosition < minPosition && velocity < -minDismissVelocityPxPerSecond) {
+                return 0;
+            }
+            if (dividerPosition > maxPosition && velocity > minDismissVelocityPxPerSecond) {
+                return fullyExpandedPosition;
+            }
+        }
         final float minFlingVelocityPxPerSecond =
                 MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity;
-        if (dividerPosition < minPosition && velocity < -minDismissVelocityPxPerSecond) {
-            return 0;
+        if (Math.abs(velocity) >= minFlingVelocityPxPerSecond) {
+            return dividerPositionForFling(
+                    dividerPosition, minPosition, maxPosition, velocity);
         }
-        if (dividerPosition > maxPosition && velocity > minDismissVelocityPxPerSecond) {
-            return fullyExpandedPosition;
+        if (dividerPosition >= minPosition && dividerPosition <= maxPosition) {
+            return dividerPosition;
         }
-        if (Math.abs(velocity) < minFlingVelocityPxPerSecond) {
-            if (dividerPosition >= minPosition && dividerPosition <= maxPosition) {
-                return dividerPosition;
-            }
-            final int[] snapPositions = {0, minPosition, maxPosition, fullyExpandedPosition};
-            return snap(dividerPosition, snapPositions);
-        }
-        return dividerPositionForFling(
-                dividerPosition, minPosition, maxPosition, velocity);
+        return snap(
+                dividerPosition,
+                isDraggingToFullscreenAllowed
+                        ? new int[] {0, minPosition, maxPosition, fullyExpandedPosition}
+                        : new int[] {minPosition, maxPosition});
     }
 
     /**
@@ -959,7 +968,7 @@
     @VisibleForTesting
     static class Properties {
         private static final int CONFIGURATION_MASK_FOR_DIVIDER =
-                ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
+                CONFIG_DENSITY | CONFIG_WINDOW_CONFIGURATION | CONFIG_LAYOUT_DIRECTION;
         @NonNull
         private final Configuration mConfiguration;
         @NonNull
@@ -1228,6 +1237,12 @@
                             FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_SLIPPERY,
                             PixelFormat.TRANSLUCENT);
             lp.setTitle(WINDOW_NAME);
+
+            // Ensure that the divider layout is always LTR regardless of the locale, because we
+            // already considered the locale when determining the split layout direction and the
+            // computed divider line position always starts from the left. This only affects the
+            // horizontal layout and does not have any effect on the top-to-bottom layout.
+            mDividerLayout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
             mViewHost.setView(mDividerLayout, lp);
             mViewHost.relayout(lp);
         }
@@ -1379,10 +1394,16 @@
                 primaryBounds = mProperties.mIsReversedLayout ? boundsBottom : boundsTop;
                 secondaryBounds = mProperties.mIsReversedLayout ? boundsTop : boundsBottom;
             }
-            t.setWindowCrop(mPrimaryVeil, primaryBounds.width(), primaryBounds.height());
-            t.setWindowCrop(mSecondaryVeil, secondaryBounds.width(), secondaryBounds.height());
-            t.setPosition(mPrimaryVeil, primaryBounds.left, primaryBounds.top);
-            t.setPosition(mSecondaryVeil, secondaryBounds.left, secondaryBounds.top);
+            if (mPrimaryVeil != null) {
+                t.setWindowCrop(mPrimaryVeil, primaryBounds.width(), primaryBounds.height());
+                t.setPosition(mPrimaryVeil, primaryBounds.left, primaryBounds.top);
+                t.setVisibility(mPrimaryVeil, !primaryBounds.isEmpty());
+            }
+            if (mSecondaryVeil != null) {
+                t.setWindowCrop(mSecondaryVeil, secondaryBounds.width(), secondaryBounds.height());
+                t.setPosition(mSecondaryVeil, secondaryBounds.left, secondaryBounds.top);
+                t.setVisibility(mSecondaryVeil, !secondaryBounds.isEmpty());
+            }
         }
 
         private static float[] colorToFloatArray(@NonNull Color color) {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index af3c4da..4f51815 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -660,108 +660,241 @@
         // Divider position is less than minPosition and the velocity is enough to be dismissed
         assertEquals(
                 0, // Closed position
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         10 /* dividerPosition */,
                         30 /* minPosition */,
                         900 /* maxPosition */,
                         1200 /* fullyExpandedPosition */,
                         -dismissVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
 
         // Divider position is greater than maxPosition and the velocity is enough to be dismissed
         assertEquals(
                 1200, // Fully expanded position
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         1000 /* dividerPosition */,
                         30 /* minPosition */,
                         900 /* maxPosition */,
                         1200 /* fullyExpandedPosition */,
                         dismissVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
 
         // Divider position is returned when the velocity is not fast enough for fling and is in
         // between minPosition and maxPosition
         assertEquals(
                 500, // dividerPosition is not snapped
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         500 /* dividerPosition */,
                         30 /* minPosition */,
                         900 /* maxPosition */,
                         1200 /* fullyExpandedPosition */,
                         nonFlingVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
 
         // Divider position is snapped when the velocity is not fast enough for fling and larger
         // than maxPosition
         assertEquals(
                 900, // Closest position is maxPosition
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         950 /* dividerPosition */,
                         30 /* minPosition */,
                         900 /* maxPosition */,
                         1200 /* fullyExpandedPosition */,
                         nonFlingVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
 
         // Divider position is snapped when the velocity is not fast enough for fling and smaller
         // than minPosition
         assertEquals(
                 30, // Closest position is minPosition
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         20 /* dividerPosition */,
                         30 /* minPosition */,
                         900 /* maxPosition */,
                         1200 /* fullyExpandedPosition */,
                         nonFlingVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
 
         // Divider position is in the closed to maxPosition bounds and the velocity is enough for
         // backward fling
         assertEquals(
                 2000, // maxPosition
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         2200 /* dividerPosition */,
                         1000 /* minPosition */,
                         2000 /* maxPosition */,
                         2500 /* fullyExpandedPosition */,
                         -flingVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
 
         // Divider position is not in the closed to maxPosition bounds and the velocity is enough
         // for backward fling
         assertEquals(
                 1000, // minPosition
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         1200 /* dividerPosition */,
                         1000 /* minPosition */,
                         2000 /* maxPosition */,
                         2500 /* fullyExpandedPosition */,
                         -flingVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
 
         // Divider position is in the closed to minPosition bounds and the velocity is enough for
         // forward fling
         assertEquals(
                 1000, // minPosition
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         500 /* dividerPosition */,
                         1000 /* minPosition */,
                         2000 /* maxPosition */,
                         2500 /* fullyExpandedPosition */,
                         flingVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
 
         // Divider position is not in the closed to minPosition bounds and the velocity is enough
         // for forward fling
         assertEquals(
                 2000, // maxPosition
-                DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+                DividerPresenter.dividerPositionWithPositionOptions(
                         1200 /* dividerPosition */,
                         1000 /* minPosition */,
                         2000 /* maxPosition */,
                         2500 /* fullyExpandedPosition */,
                         flingVelocity,
-                        displayDensity));
+                        displayDensity,
+                        true /* isDraggingToFullscreenAllowed */));
+    }
+
+    @Test
+    public void testDividerPositionWithDraggingToFullscreenNotAllowed() {
+        final float displayDensity = 600F;
+        final float nonFlingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity - 10f;
+        final float flingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity + 10f;
+
+        // Divider position is returned when the velocity is not fast enough for fling and is in
+        // between minPosition and maxPosition
+        assertEquals(
+                500, // dividerPosition is not snapped
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        500 /* dividerPosition */,
+                        30 /* minPosition */,
+                        900 /* maxPosition */,
+                        1200 /* fullyExpandedPosition */,
+                        nonFlingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
+
+        // Divider position is snapped when the velocity is not fast enough for fling and larger
+        // than maxPosition
+        assertEquals(
+                900, // Closest position is maxPosition
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        950 /* dividerPosition */,
+                        30 /* minPosition */,
+                        900 /* maxPosition */,
+                        1200 /* fullyExpandedPosition */,
+                        nonFlingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
+
+        // Divider position is snapped when the velocity is not fast enough for fling and smaller
+        // than minPosition
+        assertEquals(
+                30, // Closest position is minPosition
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        20 /* dividerPosition */,
+                        30 /* minPosition */,
+                        900 /* maxPosition */,
+                        1200 /* fullyExpandedPosition */,
+                        nonFlingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
+
+        // Divider position is snapped when the velocity is not fast enough for fling and at the
+        // closed position
+        assertEquals(
+                30, // Closest position is minPosition
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        0 /* dividerPosition */,
+                        30 /* minPosition */,
+                        900 /* maxPosition */,
+                        1200 /* fullyExpandedPosition */,
+                        nonFlingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
+
+        // Divider position is snapped when the velocity is not fast enough for fling and at the
+        // fully expanded position
+        assertEquals(
+                900, // Closest position is maxPosition
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        1200 /* dividerPosition */,
+                        30 /* minPosition */,
+                        900 /* maxPosition */,
+                        1200 /* fullyExpandedPosition */,
+                        nonFlingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
+
+        // Divider position is in the closed to maxPosition bounds and the velocity is enough for
+        // backward fling
+        assertEquals(
+                2000, // maxPosition
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        2200 /* dividerPosition */,
+                        1000 /* minPosition */,
+                        2000 /* maxPosition */,
+                        2500 /* fullyExpandedPosition */,
+                        -flingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
+
+        // Divider position is not in the closed to maxPosition bounds and the velocity is enough
+        // for backward fling
+        assertEquals(
+                1000, // minPosition
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        1200 /* dividerPosition */,
+                        1000 /* minPosition */,
+                        2000 /* maxPosition */,
+                        2500 /* fullyExpandedPosition */,
+                        -flingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
+
+        // Divider position is in the closed to minPosition bounds and the velocity is enough for
+        // forward fling
+        assertEquals(
+                1000, // minPosition
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        500 /* dividerPosition */,
+                        1000 /* minPosition */,
+                        2000 /* maxPosition */,
+                        2500 /* fullyExpandedPosition */,
+                        flingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
+
+        // Divider position is not in the closed to minPosition bounds and the velocity is enough
+        // for forward fling
+        assertEquals(
+                2000, // maxPosition
+                DividerPresenter.dividerPositionWithPositionOptions(
+                        1200 /* dividerPosition */,
+                        1000 /* minPosition */,
+                        2000 /* maxPosition */,
+                        2500 /* fullyExpandedPosition */,
+                        flingVelocity,
+                        displayDensity,
+                        false /* isDraggingToFullscreenAllowed */));
     }
 
     private TaskFragmentContainer createMockTaskFragmentContainer(
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 7a98683..bcb1d29 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -30,5 +30,29 @@
             android:excludeFromRecents="true"
             android:launchMode="singleInstance"
             android:theme="@style/DesktopWallpaperTheme" />
+
+        <activity
+            android:name=".bubbles.shortcut.CreateBubbleShortcutActivity"
+            android:exported="true"
+            android:excludeFromRecents="true"
+            android:theme="@android:style/Theme.NoDisplay"
+            android:label="Bubbles"
+            android:icon="@drawable/ic_bubbles_shortcut_widget">
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".bubbles.shortcut.ShowBubblesActivity"
+            android:exported="true"
+            android:excludeFromRecents="true"
+            android:theme="@android:style/Theme.NoDisplay" >
+            <intent-filter>
+                <action android:name="com.android.wm.shell.bubbles.action.SHOW_BUBBLES"/>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 15f8c32..112eb61 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -111,3 +111,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "animate_bubble_size_change"
+    namespace: "multitasking"
+    description: "Turns on the animation for bubble bar icons size change"
+    bug: "335575529"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 0efdbdc..327e205 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -456,5 +456,7 @@
         override fun isStackExpanded(): Boolean = false
 
         override fun isShowingAsBubbleBar(): Boolean = false
+
+        override fun hideCurrentInputMethod() {}
     }
 }
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml
index 04ad572..a30cfb7 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_layout_background.xml
@@ -20,5 +20,6 @@
     android:shape="rectangle">
     <corners
         android:radius="@dimen/desktop_mode_maximize_menu_buttons_outline_radius"/>
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerLow"/>
     <stroke android:width="1dp" android:color="?androidprv:attr/materialColorOutlineVariant"/>
 </shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget.xml b/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget.xml
new file mode 100644
index 0000000..b208f2f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget.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.
+  -->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/ic_bubbles_shortcut_widget_background" />
+    <foreground android:drawable="@drawable/ic_bubbles_shortcut_widget_foreground" />
+</adaptive-icon>
diff --git a/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget_background.xml b/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget_background.xml
new file mode 100644
index 0000000..510221f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget_background.xml
@@ -0,0 +1,24 @@
+<?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="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+  <path
+      android:pathData="M0,0h108v108h-108z"
+      android:fillColor="#FFC20C"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget_foreground.xml b/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget_foreground.xml
new file mode 100644
index 0000000..a41b6a9
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/ic_bubbles_shortcut_widget_foreground.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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="@android:color/white">
+  <group android:scaleX="0.58"
+      android:scaleY="0.58"
+      android:translateX="5.04"
+      android:translateY="5.04">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M7.2,14.4m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M14.8,18m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M15.2,8.8m-4.8,0a4.8,4.8 0,1 1,9.6 0a4.8,4.8 0,1 1,-9.6 0"/>
+  </group>
+</vector>
\ No newline at end of file
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 9599658..7d5f9cd 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
@@ -31,23 +31,15 @@
         android:layout_height="wrap_content"
         android:orientation="vertical">
 
-        <FrameLayout
-            android:id="@+id/maximize_menu_maximize_button_layout"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:background="@drawable/desktop_mode_maximize_menu_layout_background"
-            android:padding="4dp"
+        <Button
+            android:layout_width="94dp"
+            android:layout_height="60dp"
+            android:id="@+id/maximize_menu_maximize_button"
+            style="?android:attr/buttonBarButtonStyle"
+            android:stateListAnimator="@null"
             android:layout_marginRight="8dp"
             android:layout_marginBottom="4dp"
-            android:alpha="0">
-            <Button
-                android:id="@+id/maximize_menu_maximize_button"
-                style="?android:attr/buttonBarButtonStyle"
-                android:layout_width="86dp"
-                android:layout_height="@dimen/desktop_mode_maximize_menu_button_height"
-                android:background="@drawable/desktop_mode_maximize_menu_button_background"
-                android:stateListAnimator="@null"/>
-        </FrameLayout>
+            android:alpha="0"/>
 
         <TextView
             android:id="@+id/maximize_menu_maximize_window_text"
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 1c8f5e6..8b328e2 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tik om hierdie app te herbegin vir ’n beter aansig"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Verander hierdie app se aspekverhouding in Instellings"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Verander aspekverhouding"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 81ab3ab..b005a01 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"ለተሻለ ዕይታ ይህን መተግበሪያ እንደገና ለመጀመር መታ ያድርጉ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"የዚህን መተግበሪያ ምጥጥነ ገፅታ በቅንብሮች ውስጥ ይለውጡ"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"የምጥጥነ ገፅታ ለውጥ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 3974c39..8c283d3 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"انقر لإعادة تشغيل هذا التطبيق للحصول على تجربة عرض أفضل."</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"يمكنك تغيير نسبة العرض إلى الارتفاع لهذا التطبيق من خلال \"الإعدادات\"."</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تغيير نسبة العرض إلى الارتفاع"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index a1ce1b3..ef92587 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"উন্নত ভিউ পোৱাৰ বাবে এপ্‌টো ৰিষ্টাৰ্ট কৰিবলৈ টিপক"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ছেটিঙলৈ গৈ এই এপ্‌টোৰ আকাৰৰ অনুপাত সলনি কৰক"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"আকাৰৰ অনুপাত সলনি কৰক"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 71dfe5a..04b2f1c 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Yaxşı görünüş üçün toxunaraq bu tətbiqi yenidən başladın"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ayarlarda bu tətbiqin tərəflər nisbətini dəyişin"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tərəflər nisbətini dəyişin"</string>
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 f483609..47bc105 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promenite razmeru ove aplikacije u Podešavanjima"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promeni razmeru"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 532ecc6..6ad7553 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Націсніце, каб перазапусціць гэту праграму для зручнейшага прагляду"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Змяніць суадносіны бакоў для гэтай праграмы ў наладах"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Змяніць суадносіны бакоў"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 8f828ba..a9e0bce 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Докоснете, за да рестартирате това приложение с цел по-добър изглед"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Променете съотношението на това приложение в „Настройки“"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Промяна на съотношението"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index e0a2ea8..29de100 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"আরও ভাল ভিউয়ের জন্য এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"সেটিংস থেকে এই অ্যাপের অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 41c72c1..5f1da75 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijenite format slike aplikacije u Postavkama"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijenite format slike"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 6792272..d70de79 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Canvia la relació d\'aspecte d\'aquesta aplicació a Configuració"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Canvia la relació d\'aspecte"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index aafb2e1..ca00fec 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -84,7 +84,11 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
-    <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
+    <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci restartujete kvůli lepšímu zobrazení"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Změnit v Nastavení poměr stran této aplikace"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Změnit poměr stran"</string>
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 8878910..d50d2f0 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tryk for at genstarte denne app, så visningen forbedres"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Skift denne apps billedformat i Indstillinger"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Skift billedformat"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index bcdc2a9..7f44f83 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -48,8 +48,8 @@
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50 % oben"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30 % oben"</string>
     <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Vollbild unten"</string>
-    <string name="accessibility_split_left" msgid="1713683765575562458">"Links teilen"</string>
-    <string name="accessibility_split_right" msgid="8441001008181296837">"Rechts teilen"</string>
+    <string name="accessibility_split_left" msgid="1713683765575562458">"Links positionieren"</string>
+    <string name="accessibility_split_right" msgid="8441001008181296837">"Rechts positionieren"</string>
     <string name="accessibility_split_top" msgid="2789329702027147146">"Oben teilen"</string>
     <string name="accessibility_split_bottom" msgid="8694551025220868191">"Unten teilen"</string>
     <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Einhandmodus wird verwendet"</string>
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tippen, um diese App neu zu starten und die Ansicht zu verbessern"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Seitenverhältnis der App in den Einstellungen ändern"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Seitenverhältnis ändern"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 14e5e2f..a3a5ccd 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Πατήστε για να επανεκκινήσετε αυτή την εφαρμογή για καλύτερη προβολή"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Αλλάξτε τον λόγο διαστάσεων αυτής της εφαρμογής στις Ρυθμίσεις"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Αλλαγή λόγου διαστάσεων"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 7427b62..edc4f4e 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index cb9ee4f..e537f0a 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 7427b62..edc4f4e 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 7427b62..edc4f4e 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index 8498807..bdcd275 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎Bubble‎‏‎‎‏‎"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎Manage‎‏‎‎‏‎"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎Bubble dismissed.‎‏‎‎‏‎"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎Tap to restart this app for a better view‎‏‎‎‏‎"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎Change this app\'s aspect ratio in Settings‎‏‎‎‏‎"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎Change aspect ratio‎‏‎‎‏‎"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 406c1f3..8653e59 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Presiona para reiniciar esta app y tener una mejor vista"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta app en Configuración"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 0583d79..8f59c9c 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Toca para reiniciar esta aplicación y obtener una mejor vista"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta aplicación en Ajustes"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 70547f5..3d86eb4 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Puudutage, et see rakendus parema vaate jaoks taaskäivitada"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muutke selle rakenduse kuvasuhet jaotises Seaded"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Muutke kuvasuhet"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 4be35ea..4e7bdd2 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Hobeto ikusteko, sakatu hau, eta aplikazioa berrabiarazi egingo da"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Aldatu aplikazioaren aspektu-erlazioa ezarpenetan"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Aldatu aspektu-erlazioa"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 32d5f5f..3910042 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"برای داشتن نمایی بهتر، ضربه بزنید تا این برنامه بازراه‌اندازی شود"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"نسبت ابعادی این برنامه را در «تنظیمات» تغییر دهید"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تغییر نسبت ابعادی"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 6f03545..577d625 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Napauta, niin sovellus käynnistyy uudelleen paremmin näytölle sopivana"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muuta tämän sovelluksen kuvasuhdetta Asetuksissa"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Vaihda kuvasuhdetta"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 3492f13..74d822a 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Changer les proportions de cette application dans les paramètres"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier les proportions"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 4002e4d..4d14d0b 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Appuyez pour redémarrer cette appli et obtenir une meilleure vue."</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Modifiez le format de cette appli dans les Paramètres."</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier le format"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index c371f7f..e5b67c2 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia a proporción desta aplicación en Configuración"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar a proporción"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 7e3d7a3..e2a52dc 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"વધુ સારા વ્યૂ માટે, આ ઍપને ફરી શરૂ કરવા ટૅપ કરો"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"સેટિંગમાં આ ઍપનો સાપેક્ષ ગુણોત્તર બદલો"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"સાપેક્ષ ગુણોત્તર બદલો"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index cd0f4e3..f75e0e0 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"बेहतर व्यू पाने के लिए, टैप करके ऐप्लिकेशन को रीस्टार्ट करें"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिंग में जाकर इस ऐप्लिकेशन का आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 27d4cfc..ed80c50 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijeni omjer slike ove aplikacije u postavkama"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijeni omjer slike"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index a8cc5c1..32a3106 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"A jobb nézet érdekében koppintson az alkalmazás újraindításához."</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Az app méretarányát a Beállításokban módosíthatja"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Méretarány módosítása"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 7f37277..65ca704 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Փոխել հավելվածի կողմերի հարաբերակցությունը Կարգավորումներում"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Փոխել չափերի հարաբերակցությունը"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 3cf55fa..975dd72 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ubah rasio aspek aplikasi ini di Setelan"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ubah rasio aspek"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 6aa56f9..11c4718 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Ýttu til að endurræsa forritið og fá betri sýn"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Breyta myndhlutfalli þessa forrits í stillingunum"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Breyta myndhlutfalli"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 3c1d5e4..168c8cc 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tocca per riavviare l\'app e migliorare la visualizzazione"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia le proporzioni dell\'app nelle Impostazioni"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambia proporzioni"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index a0c3b3a..fd4cd1a 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"כדי לראות טוב יותר יש להקיש ולהפעיל את האפליקציה הזו מחדש"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"אפשר לשנות את יחס הגובה-רוחב של האפליקציה הזו ב\'הגדרות\'"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"שינוי יחס גובה-רוחב"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fb726c1..64ddec9 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"タップしてこのアプリを再起動すると、表示が適切になります"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"このアプリのアスペクト比を [設定] で変更します"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"アスペクト比を変更"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index e9f620a..cab8807 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"შეხებით გადატვირთეთ ეს აპი უკეთესი ხედის მისაღებად"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"შეცვალეთ ამ აპის თანაფარდობა პარამეტრებიდან"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"თანაფარდობის შეცვლა"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 34e4103..4ff5b85 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Көріністі жақсарту үшін осы қолданбаны түртіп, қайта ашыңыз."</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Осы қолданбаның арақатынасын параметрлерден өзгертуге болады."</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Арақатынасты өзгерту"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 362bbad..ba7a324 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោល​សារលេចឡើង។"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"ចុចដើម្បី​ចាប់ផ្ដើម​កម្មវិធី​នេះឡើងវិញសម្រាប់ទិដ្ឋភាពកាន់តែប្រសើរ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ផ្លាស់ប្ដូរសមាមាត្រ​របស់កម្មវិធីនេះនៅក្នុងការកំណត់"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ប្ដូរ​​សមាមាត្រ"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 77cc4a4..423e8d5 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"ಉತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಈ ಆ್ಯಪ್‌ನ ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index e8b5522..0d1c621 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"탭하면 앱을 다시 시작하여 보기를 개선합니다."</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"설정에서 앱의 가로세로 비율을 변경합니다."</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"가로세로 비율 변경"</string>
@@ -95,7 +99,7 @@
     <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"앱 위치를 조정하려면 앱 외부를 두 번 탭합니다."</string>
     <string name="letterbox_education_got_it" msgid="4057634570866051177">"확인"</string>
     <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"추가 정보는 펼쳐서 확인하세요."</string>
-    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"화면에 맞게 보도록 다시 시작할까요?"</string>
+    <string name="letterbox_restart_dialog_title" msgid="8543049527871033505">"화면에 맞게 보이도록 다시 시작할까요?"</string>
     <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"앱을 다시 시작하면 화면에 더 잘 맞게 볼 수는 있지만 진행 상황 또는 저장되지 않은 변경사항을 잃을 수도 있습니다."</string>
     <string name="letterbox_restart_cancel" msgid="1342209132692537805">"취소"</string>
     <string name="letterbox_restart_restart" msgid="8529976234412442973">"다시 시작"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 302c007..f17e9ca 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Жакшыраак көрүү үчүн бул колдонмону өчүрүп күйгүзүңүз. Ал үчүн таптап коюңуз"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Бул колдонмонун тараптарынын катнашын параметрлерден өзгөртүү"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Тараптардын катнашын өзгөртүү"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index a351963..195e4d5 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ປ່ຽນອັດຕາສ່ວນຂອງແອັບນີ້ໃນການຕັ້ງຄ່າ"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ປ່ຽນອັດຕາສ່ວນ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index e4dd739..63ad580 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Palieskite, kad iš naujo paleistumėte šią programą ir matytumėte aiškiau"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Pakeiskite šios programos kraštinių santykį"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Keisti kraštinių santykį"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 99aebf6..268d893 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Pieskarieties, lai restartētu šo lietotni un uzlabotu attēlojumu."</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Iestatījumos mainiet šīs lietotnes malu attiecību."</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mainīt malu attiecību"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index c152c60..0a0027f 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Допрете за да ја рестартирате апликацијава за подобар приказ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Промени го соодносот на апликацијава во „Поставки“"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Променување на соодносот"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 90275cd..07809e1 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"മികച്ച കാഴ്‌ചയ്‌ക്കായി ഈ ആപ്പ് റീസ്‌റ്റാർട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ഈ ആപ്പിന്റെ വീക്ഷണ അനുപാതം, ക്രമീകരണത്തിൽ മാറ്റുക"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 5e43506..99bd2df 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Харагдах байдлыг сайжруулахын тулд энэ аппыг товшиж, дахин эхлүүлнэ үү"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Энэ аппын харьцааг Тохиргоонд өөрчилнө үү"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Харьцааг өөрчлөх"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 5874bff..ac57e0a5 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"अधिक चांगल्या दृश्यासाठी हे अ‍ॅप रीस्टार्ट करण्याकरिता टॅप करा"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिंग्ज मध्ये या ॲपचा आस्पेक्ट रेशो बदला"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"आस्पेक्ट रेशो बदला"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 4de8a7b..6bc2fbb 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Tukar nisbah bidang apl ini dalam Tetapan"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tukar nisbah bidang"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 5b9e9cb..12c19ed 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဤအက်ပ်ပြန်စရန် တို့ပါ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ဆက်တင်များတွင် ဤအက်ပ်၏အချိုးအစားကို ပြောင်းရန်"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"အချိုးစား ပြောင်းရန်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 9f03d8b..1161eb6 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -84,8 +84,12 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Trykk for å starte denne appen på nytt og få en bedre visning"</string>
-    <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i innstillingene"</string>
+    <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i Innstillinger"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre høyde/bredde-forholdet"</string>
     <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
     <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index a5bd2ab..25d0337 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"अझ राम्रो भ्यू प्राप्त गर्नका लागि यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस्"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिङमा गई यो एपको एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 0cd27c5..4ad343c 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tik om deze app opnieuw op te starten voor een betere weergave"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Wijzig de beeldverhouding van deze app in Instellingen"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Beeldverhouding wijzigen"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index bf75185..966d404 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"ଏକ ଆହୁରି ଭଲ ଭ୍ୟୁ ପାଇଁ ଏହି ଆପ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ସେଟିଂସରେ ଏହି ଆପର ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 325c1e8..9feaf41 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਵਾਸਤੇ ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਇਸ ਐਪ ਦੇ ਆਕਾਰ ਅਨੁਪਾਤ ਨੂੰ ਬਦਲੋ"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index a7648c8..1c7fbf8 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Kliknij w celu zrestartowania aplikacji, aby lepiej się wyświetlała."</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Zmień proporcje obrazu aplikacji w Ustawieniach"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Zmień proporcje obrazu"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index e47d151..5c2de2a 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas Configurações"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 1210fe8..6f76525 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar esta app e ficar com uma melhor visão"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Altere o formato desta app nas Definições"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Altere o formato"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index e47d151..5c2de2a 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude o tamanho da janela deste app nas Configurações"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index ae871f3..6e85e78 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionează"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Atinge ca să repornești aplicația pentru o vizualizare mai bună"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Schimbă raportul de dimensiuni al aplicației din Setări"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Schimbă raportul de dimensiuni"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 971e146..1b41983 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Нажмите, чтобы перезапустить приложение и оптимизировать размер"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Изменить соотношение сторон приложения в настройках"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Изменить соотношение сторон"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index ef1381c..6fd37e9 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"වඩා හොඳ දසුනක් සඳහා මෙම යෙදුම යළි ඇරඹීමට තට්ටු කරන්න"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"සැකසීම් තුළ මෙම යෙදුමේ දර්ශන අනුපාතය වෙනස් කරන්න"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 55a0312..dabbf39 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Ak chcete zlepšiť zobrazenie, klepnutím túto aplikáciu reštartujte"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Zmeniť pomer strán tejto aplikácie v Nastaveniach"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Zmeniť pomer strán"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index bb123dc..3ade338 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Če želite boljši prikaz, se dotaknite za vnovični zagon te aplikacije."</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Razmerje stranic te aplikacije spremenite v nastavitvah."</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Sprememba razmerja stranic"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index c74a8cd..ee1aa00 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Trokit për ta rinisur këtë aplikacion për një pamje më të mirë"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ndrysho raportin e pamjes së këtij aplikacioni te \"Cilësimet\""</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ndrysho raportin e pamjes"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 0694a97..b2868ca 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Додирните да бисте рестартовали ову апликацију ради бољег приказа"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Промените размеру ове апликације у Подешавањима"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Промени размеру"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 8e0bcfe..66118ef 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Tryck för att starta om appen och få en bättre vy"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ändra appens bildformat i inställningarna"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ändra bildformat"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 41180ab..863b49b 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Badilisha uwiano wa programu hii katika Mipangilio"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Badilisha uwiano wa kipengele"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 01ac78d..74e0207 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"இங்கு தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கி, ஆப்ஸ் காட்டப்படும் விதத்தை இன்னும் சிறப்பாக்கலாம்"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"அமைப்புகளில் இந்த ஆப்ஸின் தோற்ற விகிதத்தை மாற்றும்"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"தோற்ற விகிதத்தை மாற்றும்"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 6224e72..3571156 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"మెరుగైన వీక్షణ కోసం ఈ యాప్‌ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేయండి"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"సెట్టింగ్‌లలో ఈ యాప్ ఆకార నిష్పత్తిని మార్చండి"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ఆకార నిష్పత్తిని మార్చండి"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index fe0b74c..4769416 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"แตะเพื่อรีสตาร์ทแอปนี้และรับมุมมองที่ดียิ่งขึ้น"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"เปลี่ยนสัดส่วนภาพของแอปนี้ในการตั้งค่า"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"เปลี่ยนอัตราส่วนกว้างยาว"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 786e99c..be18d88 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"I-tap para i-restart ang app na ito para sa mas magandang view"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Baguhin ang aspect ratio ng app na ito sa Mga Setting"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Baguhin ang aspect ratio"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index e953f58..4c8c536 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Bu uygulamayı yeniden başlatarak daha iyi bir görünüm elde etmek için dokunun"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Bu uygulamanın en boy oranını Ayarlar\'dan değiştirin"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"En boy oranını değiştir"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index fbdf42e..7cc1a04 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Натисніть, щоб перезапустити цей додаток для зручнішого перегляду"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Змінити формат для цього додатка в налаштуваннях"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Змінити формат"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 5562fa7..8b9f299 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"بہتر منظر کے لیے اس ایپ کو ری اسٹارٹ کرنے کی خاطر تھپتھپائیں"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ترتیبات میں اس ایپ کی تناسبی شرح کو تبدیل کریں"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تناسبی شرح کو تبدیل کریں"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 50e4232..55c6b32 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Yaxshiroq koʻrish maqsadida bu ilovani qayta ishga tushirish uchun bosing"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Sozlamalar orqali bu ilovaning tomonlar nisbatini oʻzgartiring"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tomonlar nisbatini oʻzgartirish"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 6da8588..07a6b6f 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Nhấn nút khởi động lại ứng dụng này để xem dễ hơn"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Thay đổi tỷ lệ khung hình của ứng dụng này thông qua phần Cài đặt"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Thay đổi tỷ lệ khung hình"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 4318caf..908095a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭消息气泡。"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"点按即可重启此应用,获得更好的视觉体验"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"在“设置”中更改此应用的宽高比"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"更改高宽比"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 72cd39d..c8550b4 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"前往「設定」變更此應用程式的長寬比"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"變更長寬比"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index c06d7b1..6704833 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"輕觸此按鈕重新啟動這個應用程式,即可獲得更良好的觀看體驗"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"前往「設定」變更這個應用程式的顯示比例"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"變更顯示比例"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 755414e..96b4faec 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -84,6 +84,10 @@
     <string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
     <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
+    <!-- no translation found for bubble_shortcut_label (666269077944378311) -->
+    <skip />
+    <!-- no translation found for bubble_shortcut_long_label (6088437544312894043) -->
+    <skip />
     <string name="restart_button_description" msgid="4564728020654658478">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono"</string>
     <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Shintsha ukubukeka kwesilinganiselo kwe-app kuMasethingi"</string>
     <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Shintsha ukubukeka kwesilinganiselo"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f27f46c..595d346 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -459,6 +459,12 @@
          start of this area. -->
     <dimen name="desktop_mode_customizable_caption_margin_end">152dp</dimen>
 
+    <!-- The default minimum allowed window width when resizing a window in desktop mode. -->
+    <dimen name="desktop_mode_minimum_window_width">386dp</dimen>
+
+    <!-- The default minimum allowed window height when resizing a window in desktop mode. -->
+    <dimen name="desktop_mode_minimum_window_height">352dp</dimen>
+
     <!-- The width of the maximize menu in desktop mode. -->
     <dimen name="desktop_mode_maximize_menu_width">228dp</dimen>
 
@@ -476,6 +482,12 @@
 
     <!-- The radius of the layout outline around the maximize menu buttons. -->
     <dimen name="desktop_mode_maximize_menu_buttons_outline_radius">6dp</dimen>
+    <!-- The stroke width of the outline around the maximize menu buttons. -->
+    <dimen name="desktop_mode_maximize_menu_buttons_outline_stroke">1dp</dimen>
+    <!-- The radius of the inner fill of the maximize menu buttons. -->
+    <dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen>
+    <!-- The padding between the outline and fill of the maximize menu buttons. -->
+    <dimen name="desktop_mode_maximize_menu_buttons_fill_padding">4dp</dimen>
 
     <!-- The corner radius of the maximize menu. -->
     <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index bf654d9..4784674 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -182,6 +182,12 @@
     <!-- Content description to tell the user a bubble has been dismissed. -->
     <string name="accessibility_bubble_dismissed">Bubble dismissed.</string>
 
+    <!-- Label used to for bubbles shortcut [CHAR_LIMIT=10] -->
+    <string name="bubble_shortcut_label">Bubbles</string>
+
+    <!-- Longer label used to for bubbles shortcut, shown if there is enough space [CHAR_LIMIT=25] -->
+    <string name="bubble_shortcut_long_label">Show Bubbles</string>
+
     <!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
     <string name="restart_button_description">Tap to restart this app for a better view</string>
 
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
index 8d8655a..4876f32 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
@@ -67,6 +67,10 @@
     private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
             "persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
 
+    /** Whether the desktop density override is enabled. */
+    public static final boolean DESKTOP_DENSITY_OVERRIDE_ENABLED =
+            SystemProperties.getBoolean("persist.wm.debug.desktop_mode_density_enabled", false);
+
     /** Override density for tasks when they're inside the desktop. */
     public static final int DESKTOP_DENSITY_OVERRIDE =
             SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 284);
@@ -157,9 +161,23 @@
     }
 
     /**
-     * Return {@code true} if the override desktop density is set.
+     * Return {@code true} if the override desktop density is enabled and valid.
      */
-    public static boolean isDesktopDensityOverrideSet() {
+    public static boolean useDesktopOverrideDensity() {
+        return isDesktopDensityOverrideEnabled() && isValidDesktopDensityOverrideSet();
+    }
+
+    /**
+     * Return {@code true} if the override desktop density is enabled.
+     */
+    private static boolean isDesktopDensityOverrideEnabled() {
+        return DESKTOP_DENSITY_OVERRIDE_ENABLED;
+    }
+
+    /**
+     * Return {@code true} if the override desktop density is set and within a valid range.
+     */
+    private static boolean isValidDesktopDensityOverrideSet() {
         return DESKTOP_DENSITY_OVERRIDE >= DESKTOP_DENSITY_MIN
                 && DESKTOP_DENSITY_OVERRIDE <= DESKTOP_DENSITY_MAX;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index a426b20..5a42817 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -31,6 +31,7 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.util.ArraySet;
@@ -45,6 +46,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationAdapter.SnapshotAdapter;
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.shared.TransitionUtil;
@@ -398,7 +400,15 @@
         // This is because the TaskFragment surface/change won't contain the Activity's before its
         // reparent.
         Animation changeAnimation = null;
-        Rect parentBounds = new Rect();
+        final Rect parentBounds = new Rect();
+        // We use a single boolean value to record the backdrop override because the override used
+        // for overlay and we restrict to single overlay animation. We should fix the assumption
+        // if we allow multiple overlay transitions.
+        // The backdrop logic is mainly for animations of split animations. The backdrop should be
+        // disabled if there is any open/close target in the same transition as the change target.
+        // However, the overlay change animation usually contains one change target, and shows
+        // backdrop unexpectedly.
+        Boolean overrideShowBackdrop = null;
         for (TransitionInfo.Change change : info.getChanges()) {
             if (change.getMode() != TRANSIT_CHANGE
                     || change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
@@ -421,17 +431,17 @@
                 }
             }
 
-            // The TaskFragment may be enter/exit split, so we take the union of both as the parent
-            // size.
-            parentBounds.union(boundsAnimationChange.getStartAbsBounds());
-            parentBounds.union(boundsAnimationChange.getEndAbsBounds());
-            if (boundsAnimationChange != change) {
-                // Union the change starting bounds in case the activity is resized and reparented
-                // to a TaskFragment. In that case, the TaskFragment may not cover the activity's
-                // starting bounds.
-                parentBounds.union(change.getStartAbsBounds());
+            final TransitionInfo.AnimationOptions options = boundsAnimationChange
+                    .getAnimationOptions();
+            if (options != null) {
+                final Animation overrideAnimation = mAnimationSpec.loadCustomAnimationFromOptions(
+                        options, TRANSIT_CHANGE);
+                if (overrideAnimation != null) {
+                    overrideShowBackdrop = overrideAnimation.getShowBackdrop();
+                }
             }
 
+            calculateParentBounds(change, boundsAnimationChange, parentBounds);
             // There are two animations in the array. The first one is for the start leash
             // (snapshot), and the second one is for the end leash (TaskFragment).
             final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
@@ -466,7 +476,7 @@
 
         // If there is no corresponding open/close window with the change, we should show background
         // color to cover the empty part of the screen.
-        boolean shouldShouldBackgroundColor = true;
+        boolean shouldShowBackgroundColor = true;
         // Handle the other windows that don't have bounds change in the same transition.
         for (TransitionInfo.Change change : info.getChanges()) {
             if (handledChanges.contains(change)) {
@@ -483,16 +493,18 @@
                 animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
             } else if (TransitionUtil.isClosingType(change.getMode())) {
                 animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds);
-                shouldShouldBackgroundColor = false;
+                shouldShowBackgroundColor = false;
             } else {
                 animation = mAnimationSpec.createChangeBoundsOpenAnimation(change, parentBounds);
-                shouldShouldBackgroundColor = false;
+                shouldShowBackgroundColor = false;
             }
             adapters.add(new ActivityEmbeddingAnimationAdapter(animation, change,
                     TransitionUtil.getRootFor(change, info)));
         }
 
-        if (shouldShouldBackgroundColor && changeAnimation != null) {
+        shouldShowBackgroundColor = overrideShowBackdrop != null
+                ? overrideShowBackdrop : shouldShowBackgroundColor;
+        if (shouldShowBackgroundColor && changeAnimation != null) {
             // Change animation may leave part of the screen empty. Show background color to cover
             // that.
             changeAnimation.setShowBackdrop(true);
@@ -502,6 +514,39 @@
     }
 
     /**
+     * Calculates parent bounds of the animation target by {@code change}.
+     */
+    @VisibleForTesting
+    static void calculateParentBounds(@NonNull TransitionInfo.Change change,
+              @NonNull TransitionInfo.Change boundsAnimationChange, @NonNull Rect outParentBounds) {
+        if (Flags.activityEmbeddingOverlayPresentationFlag()) {
+            final Point endParentSize = change.getEndParentSize();
+            if (endParentSize.equals(0, 0)) {
+                return;
+            }
+            final Point endRelPosition = change.getEndRelOffset();
+            final Point endAbsPosition = new Point(change.getEndAbsBounds().left,
+                    change.getEndAbsBounds().top);
+            final Point parentEndAbsPosition = new Point(endAbsPosition.x - endRelPosition.x,
+                    endAbsPosition.y - endRelPosition.y);
+            outParentBounds.set(parentEndAbsPosition.x, parentEndAbsPosition.y,
+                    parentEndAbsPosition.x + endParentSize.x,
+                    parentEndAbsPosition.y + endParentSize.y);
+        } else {
+            // The TaskFragment may be enter/exit split, so we take the union of both as
+            // the parent size.
+            outParentBounds.union(boundsAnimationChange.getStartAbsBounds());
+            outParentBounds.union(boundsAnimationChange.getEndAbsBounds());
+            if (boundsAnimationChange != change) {
+                // Union the change starting bounds in case the activity is resized and
+                // reparented to a TaskFragment. In that case, the TaskFragment may not cover
+                // the activity's starting bounds.
+                outParentBounds.union(change.getStartAbsBounds());
+            }
+        }
+    }
+
+    /**
      * Takes a screenshot of the given {@code screenshotChange} surface if WM Core hasn't taken one.
      * The screenshot leash should be attached to the {@code animationChange} surface which we will
      * animate later.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index b986862..8d49614 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -18,6 +18,8 @@
 
 
 import static android.app.ActivityOptions.ANIM_CUSTOM;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.window.TransitionInfo.AnimationOptions.DEFAULT_ANIMATION_RESOURCES_ID;
 
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
 import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
@@ -27,6 +29,8 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Rect;
+import android.util.Log;
+import android.view.WindowManager;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
@@ -203,7 +207,7 @@
     Animation loadOpenAnimation(@NonNull TransitionInfo info,
             @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
         final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
-        final Animation customAnimation = loadCustomAnimation(info, change, isEnter);
+        final Animation customAnimation = loadCustomAnimation(info, change);
         final Animation animation;
         if (customAnimation != null) {
             animation = customAnimation;
@@ -230,7 +234,7 @@
     Animation loadCloseAnimation(@NonNull TransitionInfo info,
             @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
         final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
-        final Animation customAnimation = loadCustomAnimation(info, change, isEnter);
+        final Animation customAnimation = loadCustomAnimation(info, change);
         final Animation animation;
         if (customAnimation != null) {
             animation = customAnimation;
@@ -263,18 +267,40 @@
 
     @Nullable
     private Animation loadCustomAnimation(@NonNull TransitionInfo info,
-            @NonNull TransitionInfo.Change change, boolean isEnter) {
+            @NonNull TransitionInfo.Change change) {
         final TransitionInfo.AnimationOptions options;
         if (Flags.moveAnimationOptionsToChange()) {
             options = change.getAnimationOptions();
         } else {
             options = info.getAnimationOptions();
         }
+        return loadCustomAnimationFromOptions(options, change.getMode());
+    }
+
+    @Nullable
+    Animation loadCustomAnimationFromOptions(@Nullable TransitionInfo.AnimationOptions options,
+             @WindowManager.TransitionType int mode) {
         if (options == null || options.getType() != ANIM_CUSTOM) {
             return null;
         }
+        final int resId;
+        if (TransitionUtil.isOpeningType(mode)) {
+            resId = options.getEnterResId();
+        } else if (TransitionUtil.isClosingType(mode)) {
+            resId = options.getExitResId();
+        } else if (mode == TRANSIT_CHANGE) {
+            resId = options.getChangeResId();
+        } else {
+            Log.w(TAG, "Unknown transit type:" + mode);
+            resId = DEFAULT_ANIMATION_RESOURCES_ID;
+        }
+        // Use the default animation if the resources ID is not specified.
+        if (resId == DEFAULT_ANIMATION_RESOURCES_ID) {
+            return null;
+        }
+
         final Animation anim = mTransitionAnimation.loadAnimationRes(options.getPackageName(),
-                isEnter ? options.getEnterResId() : options.getExitResId());
+                resId);
         if (anim != null) {
             return anim;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 5600664..7041ea3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -18,7 +18,6 @@
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME;
 import static com.android.window.flags.Flags.predictiveBackSystemAnims;
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
 
@@ -31,6 +30,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.net.Uri;
 import android.os.Bundle;
@@ -41,7 +41,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings.Global;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.IRemoteAnimationRunner;
 import android.view.InputDevice;
@@ -49,6 +48,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
 import android.window.BackAnimationAdapter;
 import android.window.BackEvent;
 import android.window.BackMotionEvent;
@@ -63,7 +63,6 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.view.AppearanceRegion;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.FlingAnimationUtils;
 import com.android.wm.shell.common.ExternalInterfaceBinder;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
@@ -88,15 +87,6 @@
     public static final boolean IS_ENABLED =
             SystemProperties.getInt("persist.wm.debug.predictive_back",
                     SETTING_VALUE_ON) == SETTING_VALUE_ON;
-    public static final float FLING_MAX_LENGTH_SECONDS = 0.1f;     // 100ms
-    public static final float FLING_SPEED_UP_FACTOR = 0.6f;
-
-    /**
-     * The maximum additional progress in case of fling gesture.
-     * The end animation starts after the user lifts the finger from the screen, we continue to
-     * fire {@link BackEvent}s until the velocity reaches 0.
-     */
-    private static final float MAX_FLING_PROGRESS = 0.3f; /* 30% of the screen */
 
     /** Predictive back animation developer option */
     private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
@@ -119,8 +109,6 @@
     private boolean mPointersPilfered = false;
     private final boolean mRequirePointerPilfer;
 
-    private final FlingAnimationUtils mFlingAnimationUtils;
-
     /** Registry for the back animations */
     private final ShellBackAnimationRegistry mShellBackAnimationRegistry;
 
@@ -133,6 +121,9 @@
     private final ShellCommandHandler mShellCommandHandler;
     private final ShellExecutor mShellExecutor;
     private final Handler mBgHandler;
+    private final WindowManager mWindowManager;
+    @VisibleForTesting
+    final Rect mTouchableArea = new Rect();
 
     /**
      * Tracks the current user back gesture.
@@ -233,14 +224,11 @@
         mBgHandler = bgHandler;
         shellInit.addInitCallback(this::onInit, this);
         mAnimationBackground = backAnimationBackground;
-        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
-        mFlingAnimationUtils = new FlingAnimationUtils.Builder(displayMetrics)
-                .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS)
-                .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
-                .build();
         mShellBackAnimationRegistry = shellBackAnimationRegistry;
         mLatencyTracker = LatencyTracker.getInstance(mContext);
         mShellCommandHandler = shellCommandHandler;
+        mWindowManager = context.getSystemService(WindowManager.class);
+        updateTouchableArea();
     }
 
     private void onInit() {
@@ -302,6 +290,11 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         mShellBackAnimationRegistry.onConfigurationChanged(newConfig);
+        updateTouchableArea();
+    }
+
+    private void updateTouchableArea() {
+        mTouchableArea.set(mWindowManager.getCurrentWindowMetrics().getBounds());
     }
 
     @Override
@@ -435,11 +428,19 @@
         if (!shouldDispatchToAnimator && mActiveCallback != null) {
             mCurrentTracker.updateStartLocation();
             tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
+            if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
+                tryPilferPointers();
+            }
         } else if (shouldDispatchToAnimator) {
             tryPilferPointers();
         }
     }
 
+    private boolean isAppProgressGenerationAllowed() {
+        return mBackNavigationInfo.isAppProgressGenerationAllowed()
+                && mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea);
+    }
+
     /**
      * Called when a new motion event needs to be transferred to this
      * {@link BackAnimationController}
@@ -555,6 +556,9 @@
             // App is handling back animation. Cancel system animation latency tracking.
             cancelLatencyTracking();
             tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
+            if (!isAppProgressGenerationAllowed()) {
+                tryPilferPointers();
+            }
         }
     }
 
@@ -661,7 +665,8 @@
 
     private void dispatchOnBackProgressed(IOnBackInvokedCallback callback,
             BackMotionEvent backEvent) {
-        if (callback == null || !shouldDispatchToAnimator()) {
+        if (callback == null || (!shouldDispatchToAnimator() && mBackNavigationInfo != null
+                && isAppProgressGenerationAllowed())) {
             return;
         }
         try {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 9114c7a..a3111b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -354,7 +354,7 @@
         matrix.postScale(scale, scale, scalePivotX, 0f)
         matrix.postTranslate(tempRectF.left, tempRectF.top)
         transaction
-            .setAlpha(leash, keepMinimumAlpha(alpha))
+            .setAlpha(leash, alpha)
             .setMatrix(leash, matrix, tmpFloat9)
             .setCrop(leash, cropRect)
             .setCornerRadius(leash, cornerRadius)
@@ -562,9 +562,6 @@
     }
 }
 
-// The target will loose focus when alpha == 0, so keep a minimum value for it.
-private fun keepMinimumAlpha(transAlpha: Float) = max(transAlpha, 0.005f)
-
 private fun isDarkMode(context: Context): Boolean {
     return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
         Configuration.UI_MODE_NIGHT_YES
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 317e00a..d2c36e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -86,6 +86,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.CollectionUtils;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
@@ -93,6 +94,7 @@
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
 import com.android.wm.shell.bubbles.properties.BubbleProperties;
+import com.android.wm.shell.bubbles.shortcut.BubbleShortcutHelper;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.ExternalInterfaceBinder;
 import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -511,6 +513,10 @@
         }
         mCurrentProfiles = userProfiles;
 
+        if (Flags.enableRetrievableBubbles()) {
+            registerShortcutBroadcastReceiver();
+        }
+
         mShellController.addConfigurationChangeListener(this);
         mShellController.addExternalInterface(KEY_EXTRA_SHELL_BUBBLES,
                 this::createExternalInterface, this);
@@ -518,7 +524,7 @@
     }
 
     private ExternalInterfaceBinder createExternalInterface() {
-        return new BubbleController.IBubblesImpl(this);
+        return new IBubblesImpl(this);
     }
 
     @VisibleForTesting
@@ -592,11 +598,12 @@
      * Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
      */
     void hideCurrentInputMethod() {
+        mBubblePositioner.setImeVisible(false /* visible */, 0 /* height */);
         int displayId = mWindowManager.getDefaultDisplay().getDisplayId();
         try {
             mBarService.hideCurrentInputMethodForBubbles(displayId);
         } catch (RemoteException e) {
-            e.printStackTrace();
+            Log.e(TAG, "Failed to hide IME", e);
         }
     }
 
@@ -986,6 +993,25 @@
         }
     };
 
+    private void registerShortcutBroadcastReceiver() {
+        IntentFilter shortcutFilter = new IntentFilter();
+        shortcutFilter.addAction(BubbleShortcutHelper.ACTION_SHOW_BUBBLES);
+        ProtoLog.d(WM_SHELL_BUBBLES, "register broadcast receive for bubbles shortcut");
+        mContext.registerReceiver(mShortcutBroadcastReceiver, shortcutFilter,
+                Context.RECEIVER_NOT_EXPORTED);
+    }
+
+    private final BroadcastReceiver mShortcutBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            ProtoLog.v(WM_SHELL_BUBBLES, "receive broadcast to show bubbles %s",
+                    intent.getAction());
+            if (BubbleShortcutHelper.ACTION_SHOW_BUBBLES.equals(intent.getAction())) {
+                mMainExecutor.execute(() -> showBubblesFromShortcut());
+            }
+        }
+    };
+
     /**
      * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
      * added in the meantime.
@@ -1457,8 +1483,9 @@
             SynchronousScreenCaptureListener screenCaptureListener) {
         try {
             ScreenCapture.CaptureArgs args = null;
-            if (mStackView != null) {
-                ViewRootImpl viewRoot = mStackView.getViewRootImpl();
+            View viewToUse = mStackView != null ? mStackView : mLayerView;
+            if (viewToUse != null) {
+                ViewRootImpl viewRoot = viewToUse.getViewRootImpl();
                 if (viewRoot != null) {
                     SurfaceControl bubbleLayer = viewRoot.getSurfaceControl();
                     if (bubbleLayer != null) {
@@ -1550,6 +1577,12 @@
                     Log.w(TAG, "Tried to add a bubble to the stack but the stack is null");
                 }
             };
+        } else if (mBubbleData.isExpanded() && mBubbleData.getSelectedBubble() != null) {
+            callback = b -> {
+                if (b.getKey().equals(mBubbleData.getSelectedBubbleKey())) {
+                    mLayerView.showExpandedView(b);
+                }
+            };
         }
         for (int i = mBubbleData.getBubbles().size() - 1; i >= 0; i--) {
             Bubble bubble = mBubbleData.getBubbles().get(i);
@@ -2221,6 +2254,34 @@
     }
 
     /**
+     * Show bubbles UI when triggered via shortcut.
+     *
+     * <p>When there are bubbles visible, expands the top-most bubble. When there are no bubbles
+     * visible, opens the bubbles overflow UI.
+     */
+    public void showBubblesFromShortcut() {
+        if (isStackExpanded()) {
+            ProtoLog.v(WM_SHELL_BUBBLES, "showBubblesFromShortcut: stack visible, skip");
+            return;
+        }
+        if (mBubbleData.getSelectedBubble() != null) {
+            ProtoLog.v(WM_SHELL_BUBBLES, "showBubblesFromShortcut: open selected bubble");
+            expandStackWithSelectedBubble();
+            return;
+        }
+        BubbleViewProvider bubbleToSelect = CollectionUtils.firstOrNull(mBubbleData.getBubbles());
+        if (bubbleToSelect == null) {
+            ProtoLog.v(WM_SHELL_BUBBLES, "showBubblesFromShortcut: no bubbles");
+            // make sure overflow bubbles are loaded
+            loadOverflowBubblesFromDisk();
+            bubbleToSelect = mBubbleData.getOverflow();
+        }
+        ProtoLog.v(WM_SHELL_BUBBLES, "showBubblesFromShortcut: select and open %s",
+                bubbleToSelect.getKey());
+        mBubbleData.setSelectedBubbleAndExpandStack(bubbleToSelect);
+    }
+
+    /**
      * Description of current bubble state.
      */
     private void dump(PrintWriter pw, String prefix) {
@@ -2354,6 +2415,8 @@
         @Override
         public void invalidate() {
             mController = null;
+            // Unregister the listeners to ensure any binder death recipients are unlinked
+            mListener.unregister();
         }
 
         @Override
@@ -2531,17 +2594,6 @@
 
         private CachedState mCachedState = new CachedState();
 
-        private IBubblesImpl mIBubbles;
-
-        @Override
-        public IBubbles createExternalInterface() {
-            if (mIBubbles != null) {
-                mIBubbles.invalidate();
-            }
-            mIBubbles = new IBubblesImpl(BubbleController.this);
-            return mIBubbles;
-        }
-
         @Override
         public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
             return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 874102c..761e025 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -256,11 +256,15 @@
     }
 
     /**
-     * Returns a bubble bar update populated with the current list of active bubbles.
+     * Returns a bubble bar update populated with the current list of active bubbles, expanded,
+     * and selected state.
      */
     public BubbleBarUpdate getInitialStateForBubbleBar() {
         BubbleBarUpdate initialState = mStateChange.getInitialState();
         initialState.bubbleBarLocation = mPositioner.getBubbleBarLocation();
+        initialState.expanded = mExpanded;
+        initialState.expandedChanged = mExpanded; // only matters if we're expanded
+        initialState.selectedBubbleKey = getSelectedBubbleKey();
         return initialState;
     }
 
@@ -598,7 +602,7 @@
         List<Bubble> removedBubbles = filterAllBubbles(bubble ->
                 userId == bubble.getUser().getIdentifier());
         for (Bubble b : removedBubbles) {
-            doRemove(b.getKey(), Bubbles.DISMISS_USER_REMOVED);
+            doRemove(b.getKey(), Bubbles.DISMISS_USER_ACCOUNT_REMOVED);
         }
         if (!removedBubbles.isEmpty()) {
             dispatchPendingChanges();
@@ -674,7 +678,7 @@
                 || reason == Bubbles.DISMISS_SHORTCUT_REMOVED
                 || reason == Bubbles.DISMISS_PACKAGE_REMOVED
                 || reason == Bubbles.DISMISS_USER_CHANGED
-                || reason == Bubbles.DISMISS_USER_REMOVED;
+                || reason == Bubbles.DISMISS_USER_ACCOUNT_REMOVED;
 
         int indexToRemove = indexForKey(key);
         if (indexToRemove == -1) {
@@ -912,6 +916,9 @@
             ((Bubble) bubble).markAsAccessedAt(mTimeSource.currentTimeMillis());
         }
         mSelectedBubble = bubble;
+        if (isOverflow) {
+            mShowingOverflow = true;
+        }
         mStateChange.selectedBubble = bubble;
         mStateChange.selectionChanged = true;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 4e8afcc..c7ccd50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -482,31 +482,38 @@
                 mPointerWidth, mPointerHeight, true /* pointLeft */));
         mRightPointer = new ShapeDrawable(TriangleShape.createHorizontal(
                 mPointerWidth, mPointerHeight, false /* pointLeft */));
-        if (mPointerView != null) {
-            updatePointerView();
-        }
+        updatePointerViewIfExists();
+        updateManageButtonIfExists();
+    }
 
-        if (mManageButton != null) {
-            int visibility = mManageButton.getVisibility();
-            removeView(mManageButton);
-            ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(),
-                    com.android.internal.R.style.Theme_DeviceDefault_DayNight);
-            mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate(
-                    R.layout.bubble_manage_button, this /* parent */, false /* attach */);
-            addView(mManageButton);
-            mManageButton.setVisibility(visibility);
-            post(() -> {
-                int touchAreaHeight =
-                        getResources().getDimensionPixelSize(
-                                R.dimen.bubble_manage_button_touch_area_height);
-                Rect r = new Rect();
-                mManageButton.getHitRect(r);
-                int extraTouchArea = (touchAreaHeight - r.height()) / 2;
-                r.top -= extraTouchArea;
-                r.bottom += extraTouchArea;
-                setTouchDelegate(new TouchDelegate(r, mManageButton));
-            });
+
+    /**
+     * Reinflate manage button if {@link #mManageButton} is initialized.
+     * Does nothing otherwise.
+     */
+    private void updateManageButtonIfExists() {
+        if (mManageButton == null) {
+            return;
         }
+        int visibility = mManageButton.getVisibility();
+        removeView(mManageButton);
+        ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(),
+                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+        mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate(
+                R.layout.bubble_manage_button, this /* parent */, false /* attach */);
+        addView(mManageButton);
+        mManageButton.setVisibility(visibility);
+        post(() -> {
+            int touchAreaHeight =
+                    getResources().getDimensionPixelSize(
+                            R.dimen.bubble_manage_button_touch_area_height);
+            Rect r = new Rect();
+            mManageButton.getHitRect(r);
+            int extraTouchArea = (touchAreaHeight - r.height()) / 2;
+            r.top -= extraTouchArea;
+            r.bottom += extraTouchArea;
+            setTouchDelegate(new TouchDelegate(r, mManageButton));
+        });
     }
 
     void updateFontSize() {
@@ -548,11 +555,18 @@
         if (mTaskView != null) {
             mTaskView.setCornerRadius(mCornerRadius);
         }
-        updatePointerView();
+        updatePointerViewIfExists();
+        updateManageButtonIfExists();
     }
 
-    /** Updates the size and visuals of the pointer. **/
-    private void updatePointerView() {
+    /**
+     * Updates the size and visuals of the pointer if {@link #mPointerView} is initialized.
+     * Does nothing otherwise.
+     */
+    private void updatePointerViewIfExists() {
+        if (mPointerView == null) {
+            return;
+        }
         LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams();
         if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) {
             lp.width = mPointerHeight;
@@ -1055,7 +1069,7 @@
         // Post because we need the width of the view
         post(() -> {
             mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer;
-            updatePointerView();
+            updatePointerViewIfExists();
             if (showVertically) {
                 mPointerPos.y = bubbleCenter - (mPointerWidth / 2f);
                 if (!isRtl) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index b0d3cc4..3d9bf03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -29,6 +29,7 @@
     fun setAppBubbleTaskId(key: String, taskId: Int)
     fun isStackExpanded(): Boolean
     fun isShowingAsBubbleBar(): Boolean
+    fun hideCurrentInputMethod()
 
     companion object {
         /**
@@ -73,6 +74,10 @@
                 override fun isStackExpanded(): Boolean = controller.isStackExpanded
 
                 override fun isShowingAsBubbleBar(): Boolean = controller.isShowingAsBubbleBar
+
+                override fun hideCurrentInputMethod() {
+                    controller.hideCurrentInputMethod()
+                }
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index fac9bf6..09bec8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -134,6 +134,8 @@
 
     private static final float EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT = 0.1f;
 
+    private static final float OPEN_OVERFLOW_ANIMATE_SCALE_AMOUNT = 0.5f;
+
     private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
 
     /** Minimum alpha value for scrim when alpha is being changed via drag */
@@ -1549,10 +1551,20 @@
     }
 
     private void updateOverflowVisibility() {
-        mBubbleOverflow.setVisible(mShowingOverflow
-                && (mIsExpanded || mBubbleData.isShowingOverflow())
-                ? VISIBLE
-                : GONE);
+        int visibility = GONE;
+        if (mShowingOverflow) {
+            if (mIsExpanded || mBubbleData.isShowingOverflow()) {
+                visibility = VISIBLE;
+            }
+        }
+        if (Flags.enableRetrievableBubbles()) {
+            if (BubbleOverflow.KEY.equals(mBubbleData.getSelectedBubbleKey())
+                    && !mBubbleData.hasBubbles()) {
+                // Hide overflow bubble icon if it is the only bubble
+                visibility = GONE;
+            }
+        }
+        mBubbleOverflow.setVisible(visibility);
     }
 
     private void updateOverflowDotVisibility(boolean expanding) {
@@ -2147,6 +2159,13 @@
         if (mIsExpanded) {
             hideCurrentInputMethod();
 
+            if (Flags.enableRetrievableBubbles()) {
+                if (mBubbleData.getBubbles().size() == 1) {
+                    // First bubble, check if overflow visibility needs to change
+                    updateOverflowVisibility();
+                }
+            }
+
             // Make the container of the expanded view transparent before removing the expanded view
             // from it. Otherwise a punch hole created by {@link android.view.SurfaceView} in the
             // expanded view becomes visible on the screen. See b/126856255
@@ -2215,6 +2234,16 @@
     }
 
     /**
+     * Check if we only have overflow expanded. Which is the case when we are launching bubbles from
+     * background.
+     */
+    private boolean isOnlyOverflowExpanded() {
+        boolean overflowExpanded = mExpandedBubble != null && BubbleOverflow.KEY.equals(
+                mExpandedBubble.getKey());
+        return overflowExpanded && !mBubbleData.hasBubbles();
+    }
+
+    /**
      * Monitor for swipe up gesture that is used to collapse expanded view
      */
     void startMonitoringSwipeUpGesture() {
@@ -2324,7 +2353,6 @@
      * not.
      */
     void hideCurrentInputMethod() {
-        mPositioner.setImeVisible(false, 0);
         mManager.hideCurrentInputMethod();
     }
 
@@ -2434,7 +2462,7 @@
         ProtoLog.d(WM_SHELL_BUBBLES, "animateExpansion, expandedBubble=%s",
                 mExpandedBubble != null ? mExpandedBubble.getKey() : "null");
         cancelDelayedExpandCollapseSwitchAnimations();
-        final boolean showVertically = mPositioner.showBubblesVertically();
+
         mIsExpanded = true;
         if (isStackEduVisible()) {
             mStackEduView.hide(true /* fromExpansion */);
@@ -2444,8 +2472,17 @@
         showScrim(true, null /* runnable */);
         updateBubbleShadows(mIsExpanded);
         mBubbleContainer.setActiveController(mExpandedAnimationController);
-        updateBadges(false /* setBadgeForCollapsedStack */);
         updateOverflowVisibility();
+
+        if (Flags.enableRetrievableBubbles() && isOnlyOverflowExpanded()) {
+            animateOverflowExpansion();
+        } else {
+            animateBubbleExpansion();
+        }
+    }
+
+    private void animateBubbleExpansion() {
+        updateBadges(false /* setBadgeForCollapsedStack */);
         updatePointerPosition(false /* forIme */);
         if (Flags.enableBubbleStashing()) {
             mBubbleContainer.animate().translationX(0).start();
@@ -2469,6 +2506,7 @@
         mExpandedViewContainer.setTranslationY(translationY);
         mExpandedViewContainer.setAlpha(1f);
 
+        final boolean showVertically = mPositioner.showBubblesVertically();
         // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles
         // that are animating farther, so that the expanded view doesn't move as much.
         final float relevantStackPosition = showVertically
@@ -2561,6 +2599,47 @@
         mMainExecutor.executeDelayed(mDelayedAnimation, startDelay);
     }
 
+    /**
+     * Animate expansion of overflow view when it is shown from the bubble shortcut.
+     * <p>
+     * Animates the view with a scale originating from the center of the view.
+     */
+    private void animateOverflowExpansion() {
+        PointF bubbleXY = mPositioner.getExpandedBubbleXY(0, getState());
+        final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
+                mPositioner.showBubblesVertically() ? bubbleXY.y : bubbleXY.x);
+        mExpandedViewContainer.setTranslationX(0f);
+        mExpandedViewContainer.setTranslationY(translationY);
+        mExpandedViewContainer.setAlpha(1f);
+
+        boolean stackOnLeft = mPositioner.isStackOnLeft(getStackPosition());
+        float width = mPositioner.getTaskViewContentWidth(stackOnLeft);
+        float height = mPositioner.getExpandedViewHeight(mExpandedBubble);
+        float scale = 1f - OPEN_OVERFLOW_ANIMATE_SCALE_AMOUNT;
+        // Scale from the center of the view
+        mExpandedViewContainerMatrix.setScale(scale, scale, width / 2f, height / 2f);
+        mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
+        mExpandedViewAlphaAnimator.start();
+        PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel();
+        PhysicsAnimator.getInstance(mExpandedViewContainerMatrix)
+                .spring(AnimatableScaleMatrix.SCALE_X,
+                        AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+                        mScaleInSpringConfig)
+                .spring(AnimatableScaleMatrix.SCALE_Y,
+                        AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f),
+                        mScaleInSpringConfig)
+                .addUpdateListener((target, values) -> {
+                    mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
+                }).withEndActions(() -> {
+                    mExpandedViewContainer.setAnimationMatrix(null);
+                    afterExpandedViewAnimation();
+                    BubbleExpandedView expandedView = getExpandedView();
+                    if (expandedView != null) {
+                        expandedView.setSurfaceZOrderedOnTop(false);
+                    }
+                }).start();
+    }
+
     private void animateCollapse() {
         cancelDelayedExpandCollapseSwitchAnimations();
         ProtoLog.d(WM_SHELL_BUBBLES, "animateCollapse");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 21b70b8..0b66bcb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -161,6 +161,11 @@
             // The taskId is saved to use for removeTask, preventing appearance in recent tasks.
             mTaskId = taskId;
 
+            if (mBubble != null && mBubble.isAppBubble()) {
+                // Let the controller know sooner what the taskId is.
+                mExpandedViewManager.setAppBubbleTaskId(mBubble.getKey(), mTaskId);
+            }
+
             // With the task org, the taskAppeared callback will only happen once the task has
             // already drawn
             mListener.onTaskCreated();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 1d053f9..82af88d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -61,7 +61,7 @@
             DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
             DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
             DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
-            DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED,
+            DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_ACCOUNT_REMOVED,
             DISMISS_SWITCH_TO_STACK})
     @Target({FIELD, LOCAL_VARIABLE, PARAMETER})
     @interface DismissReason {
@@ -82,7 +82,7 @@
     int DISMISS_PACKAGE_REMOVED = 13;
     int DISMISS_NO_BUBBLE_UP = 14;
     int DISMISS_RELOAD_FROM_DISK = 15;
-    int DISMISS_USER_REMOVED = 16;
+    int DISMISS_USER_ACCOUNT_REMOVED = 16;
     int DISMISS_SWITCH_TO_STACK = 17;
 
     /** Returns a binder that can be passed to an external process to manipulate Bubbles. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index 271fb9a..972dce5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -82,6 +82,7 @@
     private static final int INVALID_TASK_ID = -1;
 
     private BubbleExpandedViewManager mManager;
+    private BubblePositioner mPositioner;
     private boolean mIsOverflow;
     private BubbleTaskViewHelper mBubbleTaskViewHelper;
     private BubbleBarMenuViewController mMenuViewController;
@@ -160,6 +161,7 @@
             boolean isOverflow,
             @Nullable BubbleTaskView bubbleTaskView) {
         mManager = expandedViewManager;
+        mPositioner = positioner;
         mIsOverflow = isOverflow;
 
         if (mIsOverflow) {
@@ -207,7 +209,7 @@
 
             @Override
             public void onDismissBubble(Bubble bubble) {
-                mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED);
+                mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE);
             }
         });
         mHandleView.setOnClickListener(view -> {
@@ -290,15 +292,27 @@
     }
 
     /**
-     * Hides the current modal menu view or collapses the bubble stack.
-     * Called from {@link BubbleBarLayerView}
+     * Hides the current modal menu if it is visible
+     * @return {@code true} if menu was visible and is hidden
      */
-    public void hideMenuOrCollapse() {
+    public boolean hideMenuIfVisible() {
         if (mMenuViewController.isMenuVisible()) {
-            mMenuViewController.hideMenu(/* animated = */ true);
-        } else {
-            mManager.collapseStack();
+            mMenuViewController.hideMenu(true /* animated */);
+            return true;
         }
+        return false;
+    }
+
+    /**
+     * Hides the IME if it is visible
+     * @return {@code true} if IME was visible
+     */
+    public boolean hideImeIfVisible() {
+        if (mPositioner.isImeVisible()) {
+            mManager.hideCurrentInputMethod();
+            return true;
+        }
+        return false;
     }
 
     /** Updates the bubble shown in the expanded view. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 123cc7e..badc409 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -132,7 +132,7 @@
                     }
                 });
 
-        setOnClickListener(view -> hideMenuOrCollapse());
+        setOnClickListener(view -> hideModalOrCollapse());
     }
 
     @Override
@@ -217,7 +217,7 @@
 
                 @Override
                 public void onBackPressed() {
-                    hideMenuOrCollapse();
+                    hideModalOrCollapse();
                 }
             });
 
@@ -344,15 +344,23 @@
         addView(mDismissView);
     }
 
-    /** Hides the current modal education/menu view, expanded view or collapses the bubble stack */
-    private void hideMenuOrCollapse() {
+    /** Hides the current modal education/menu view, IME or collapses the expanded view */
+    private void hideModalOrCollapse() {
         if (mEducationViewController.isEducationVisible()) {
             mEducationViewController.hideEducation(/* animated = */ true);
-        } else if (isExpanded() && mExpandedView != null) {
-            mExpandedView.hideMenuOrCollapse();
-        } else {
-            mBubbleController.collapseStack();
+            return;
         }
+        if (isExpanded() && mExpandedView != null) {
+            boolean menuHidden = mExpandedView.hideMenuIfVisible();
+            if (menuHidden) {
+                return;
+            }
+            boolean imeHidden = mExpandedView.hideImeIfVisible();
+            if (imeHidden) {
+                return;
+            }
+        }
+        mBubbleController.collapseStack();
     }
 
     /** Updates the expanded view size and position. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/BubbleShortcutHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/BubbleShortcutHelper.kt
new file mode 100644
index 0000000..efa1238
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/BubbleShortcutHelper.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.bubbles.shortcut
+
+import android.content.Context
+import android.content.pm.ShortcutInfo
+import android.graphics.drawable.Icon
+import com.android.wm.shell.R
+
+/** Helper class for creating a shortcut to open bubbles */
+object BubbleShortcutHelper {
+    const val SHORTCUT_ID = "bubbles_shortcut_id"
+    const val ACTION_SHOW_BUBBLES = "com.android.wm.shell.bubbles.action.SHOW_BUBBLES"
+
+    /** Create a shortcut that launches [ShowBubblesActivity] */
+    fun createShortcut(context: Context, icon: Icon): ShortcutInfo {
+        return ShortcutInfo.Builder(context, SHORTCUT_ID)
+            .setIntent(ShowBubblesActivity.createIntent(context))
+            .setActivity(ShowBubblesActivity.createComponent(context))
+            .setShortLabel(context.getString(R.string.bubble_shortcut_label))
+            .setLongLabel(context.getString(R.string.bubble_shortcut_long_label))
+            .setLongLived(true)
+            .setIcon(icon)
+            .build()
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/CreateBubbleShortcutActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/CreateBubbleShortcutActivity.kt
new file mode 100644
index 0000000..a124f95
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/CreateBubbleShortcutActivity.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.bubbles.shortcut
+
+import android.app.Activity
+import android.content.pm.ShortcutManager
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import com.android.wm.shell.Flags
+import com.android.wm.shell.R
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES
+import com.android.wm.shell.util.KtProtoLog
+
+/** Activity to create a shortcut to open bubbles */
+class CreateBubbleShortcutActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        if (Flags.enableRetrievableBubbles()) {
+            KtProtoLog.d(WM_SHELL_BUBBLES, "Creating a shortcut for bubbles")
+            createShortcut()
+        }
+        finish()
+    }
+
+    private fun createShortcut() {
+        val icon = Icon.createWithResource(this, R.drawable.ic_bubbles_shortcut_widget)
+        // TODO(b/340337839): shortcut shows the sysui icon
+        val shortcutInfo = BubbleShortcutHelper.createShortcut(this, icon)
+        val shortcutManager = getSystemService(ShortcutManager::class.java)
+        val shortcutIntent = shortcutManager?.createShortcutResultIntent(shortcutInfo)
+        if (shortcutIntent != null) {
+            setResult(RESULT_OK, shortcutIntent)
+        } else {
+            setResult(RESULT_CANCELED)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/ShowBubblesActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/ShowBubblesActivity.kt
new file mode 100644
index 0000000..ae7940c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/shortcut/ShowBubblesActivity.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.bubbles.shortcut
+
+import android.app.Activity
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import com.android.wm.shell.Flags
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES
+import com.android.wm.shell.util.KtProtoLog
+
+/** Activity that sends a broadcast to open bubbles */
+class ShowBubblesActivity : Activity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        if (Flags.enableRetrievableBubbles()) {
+            val intent =
+                Intent().apply {
+                    action = BubbleShortcutHelper.ACTION_SHOW_BUBBLES
+                    // Set the package as the receiver is not exported
+                    `package` = packageName
+                }
+            KtProtoLog.v(WM_SHELL_BUBBLES, "Sending broadcast to show bubbles")
+            sendBroadcast(intent)
+        }
+        finish()
+    }
+
+    companion object {
+        /** Create intent to launch this activity */
+        fun createIntent(context: Context): Intent {
+            return Intent(context, ShowBubblesActivity::class.java).apply {
+                action = BubbleShortcutHelper.ACTION_SHOW_BUBBLES
+            }
+        }
+
+        /** Create component for this activity */
+        fun createComponent(context: Context): ComponentName {
+            return ComponentName(context, ShowBubblesActivity::class.java)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 57e95d6..f4ac5f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -538,8 +538,10 @@
 
                 @Override
                 public void onAnimationStart(Animator animation) {
+                    ValueAnimator valueAnimator = (ValueAnimator) animation;
+                    float value = (float) valueAnimator.getAnimatedValue();
                     SurfaceControl.Transaction t = mTransactionPool.acquire();
-                    t.setPosition(mImeSourceControl.getLeash(), x, startY);
+                    t.setPosition(mImeSourceControl.getLeash(), x, value);
                     if (DEBUG) {
                         Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
                                 + imeTop(hiddenY) + "->" + imeTop(shownY)
@@ -549,7 +551,7 @@
                             imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t);
                     mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0;
                     final float alpha = (mAnimateAlpha || isFloating)
-                            ? (startY - hiddenY) / (shownY - hiddenY)
+                            ? (value - hiddenY) / (shownY - hiddenY)
                             : 1.f;
                     t.setAlpha(mImeSourceControl.getLeash(), alpha);
                     if (mAnimationDirection == DIRECTION_SHOW) {
@@ -560,7 +562,7 @@
                     if (DEBUG_IME_VISIBILITY) {
                         EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
                                 mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
-                                mDisplayId, mAnimationDirection, alpha, startY , endY,
+                                mDisplayId, mAnimationDirection, alpha, value, endY,
                                 Objects.toString(mImeSourceControl.getLeash()),
                                 Objects.toString(mImeSourceControl.getInsetsHint()),
                                 Objects.toString(mImeSourceControl.getSurfacePosition()),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java
deleted file mode 100644
index b29058b..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExecutorUtils.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.common;
-
-import android.Manifest;
-import android.util.Slog;
-
-import java.util.function.Consumer;
-
-/**
- * Helpers for working with executors
- */
-public class ExecutorUtils {
-
-    /**
-     * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
-     * callback.
-     */
-    public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
-            String log, Consumer<T> callback) {
-        executeRemoteCallWithTaskPermission(controllerInstance, log, callback,
-                false /* blocking */);
-    }
-
-    /**
-     * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
-     * callback.
-     */
-    public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
-            String log, Consumer<T> callback, boolean blocking) {
-        if (controllerInstance == null) return;
-
-        final RemoteCallable<T> controller = controllerInstance;
-        controllerInstance.getContext().enforceCallingPermission(
-                Manifest.permission.MANAGE_ACTIVITY_TASKS, log);
-        if (blocking) {
-            try {
-                controllerInstance.getRemoteCallExecutor().executeBlocking(() -> {
-                    callback.accept((T) controller);
-                });
-            } catch (InterruptedException e) {
-                Slog.e("ExecutorUtils", "Remote call failed", e);
-            }
-        } else {
-            controllerInstance.getRemoteCallExecutor().execute(() -> {
-                callback.accept((T) controller);
-            });
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java
index aa5b0cb..d6f4d81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ExternalInterfaceBinder.java
@@ -16,7 +16,11 @@
 
 package com.android.wm.shell.common;
 
+import android.Manifest;
 import android.os.IBinder;
+import android.util.Slog;
+
+import java.util.function.Consumer;
 
 /**
  * An interface for binders which can be registered to be sent to other processes.
@@ -31,4 +35,40 @@
      * Returns the IBinder to send.
      */
     IBinder asBinder();
+
+    /**
+     * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
+     * callback.
+     */
+    default <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
+            String log, Consumer<T> callback) {
+        executeRemoteCallWithTaskPermission(controllerInstance, log, callback,
+                false /* blocking */);
+    }
+
+    /**
+     * Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
+     * callback.
+     */
+    default <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
+            String log, Consumer<T> callback, boolean blocking) {
+        if (controllerInstance == null) return;
+
+        final RemoteCallable<T> controller = controllerInstance;
+        controllerInstance.getContext().enforceCallingPermission(
+                Manifest.permission.MANAGE_ACTIVITY_TASKS, log);
+        if (blocking) {
+            try {
+                controllerInstance.getRemoteCallExecutor().executeBlocking(() -> {
+                    callback.accept((T) controller);
+                });
+            } catch (InterruptedException e) {
+                Slog.e("ExternalInterfaceBinder", "Remote call failed", e);
+            }
+        } else {
+            controllerInstance.getRemoteCallExecutor().execute(() -> {
+                callback.accept((T) controller);
+            });
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java
index 30f535b..0d90fb7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/RemoteCallable.java
@@ -19,7 +19,7 @@
 import android.content.Context;
 
 /**
- * An interface for controllers that can receive remote calls.
+ * An interface for controllers (of type T) that can receive remote calls.
  */
 public interface RemoteCallable<T> {
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
index b5f25433f..e779879 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
@@ -53,9 +53,11 @@
      * @param destinationBounds the destination bounds the PiP window lands into
      * @param overlay an optional overlay to fade out after entering PiP
      * @param appBounds the bounds used to set the buffer size of the optional content overlay
+     * @param sourceRectHint the bounds to show in the transition to PiP
      */
     oneway void stopSwipePipToHome(int taskId, in ComponentName componentName,
-            in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds) = 2;
+            in Rect destinationBounds, in SurfaceControl overlay, in Rect appBounds,
+            in Rect sourceRectHint) = 2;
 
     /**
      * Notifies the swiping Activity to PiP onto home transition is aborted
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index 579a794..a09720d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -22,6 +22,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.content.pm.PackageManager
+import android.graphics.Rect
 import android.os.RemoteException
 import android.os.SystemProperties
 import android.util.DisplayMetrics
@@ -138,6 +139,30 @@
         }
     }
 
+
+    /**
+     * Returns a fake source rect hint for animation purposes when app-provided one is invalid.
+     * Resulting adjusted source rect hint lets the app icon in the content overlay to stay visible.
+     */
+    @JvmStatic
+    fun getEnterPipWithOverlaySrcRectHint(appBounds: Rect, aspectRatio: Float): Rect {
+        val appBoundsAspRatio = appBounds.width().toFloat() / appBounds.height()
+        val width: Int
+        val height: Int
+        var left = 0
+        var top = 0
+        if (appBoundsAspRatio < aspectRatio) {
+            width = appBounds.width()
+            height = Math.round(width / aspectRatio)
+            top = (appBounds.height() - height) / 2
+        } else {
+            height = appBounds.height()
+            width = Math.round(height * aspectRatio)
+            left = (appBounds.width() - width) / 2
+        }
+        return Rect(left, top, left + width, top + height)
+    }
+
     private var isPip2ExperimentEnabled: Boolean? = null
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 2234041..c2242a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -336,6 +336,11 @@
                 setTouching();
                 mStartPos = touchPos;
                 mMoving = false;
+                // This triggers initialization of things like the resize veil in preparation for
+                // showing it when the user moves the divider past the slop, and has to be done
+                // before onStartDragging() which starts the jank interaction tracing
+                mSplitLayout.updateDividerBounds(mSplitLayout.getDividerPosition(),
+                        false /* shouldUseParallaxEffect */);
                 mSplitLayout.onStartDragging();
                 break;
             case MotionEvent.ACTION_MOVE:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
index 835f1af..07082a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
@@ -53,7 +53,7 @@
 
     private final ShellExecutor mMainExecutor;
 
-    private boolean mIsActivityLetterboxed;
+    private boolean mIsLetterboxDoubleTapEnabled;
 
     private int mLetterboxVerticalPosition;
 
@@ -91,7 +91,7 @@
             Function<Integer, Integer> disappearTimeSupplier) {
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo;
-        mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
+        mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
         mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition;
         mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition;
         mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth;
@@ -119,7 +119,7 @@
 
     @Override
     protected boolean eligibleToShowLayout() {
-        return mIsActivityLetterboxed
+        return mIsLetterboxDoubleTapEnabled
                 && (mLetterboxVerticalPosition != -1 || mLetterboxHorizontalPosition != -1);
     }
 
@@ -142,13 +142,13 @@
     @Override
     public boolean updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
             boolean canShow) {
-        final boolean prevIsActivityLetterboxed = mIsActivityLetterboxed;
+        final boolean prevIsLetterboxDoubleTapEnabled = mIsLetterboxDoubleTapEnabled;
         final int prevLetterboxVerticalPosition = mLetterboxVerticalPosition;
         final int prevLetterboxHorizontalPosition = mLetterboxHorizontalPosition;
         final int prevTopActivityLetterboxWidth = mTopActivityLetterboxWidth;
         final int prevTopActivityLetterboxHeight = mTopActivityLetterboxHeight;
         final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo;
-        mIsActivityLetterboxed = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
+        mIsLetterboxDoubleTapEnabled = appCompatTaskInfo.isLetterboxDoubleTapEnabled;
         mLetterboxVerticalPosition = appCompatTaskInfo.topActivityLetterboxVerticalPosition;
         mLetterboxHorizontalPosition = appCompatTaskInfo.topActivityLetterboxHorizontalPosition;
         mTopActivityLetterboxWidth = appCompatTaskInfo.topActivityLetterboxWidth;
@@ -162,7 +162,7 @@
         mHasLetterboxSizeChanged = prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth
                 || prevTopActivityLetterboxHeight != mTopActivityLetterboxHeight;
 
-        if (mHasUserDoubleTapped || prevIsActivityLetterboxed != mIsActivityLetterboxed
+        if (mHasUserDoubleTapped || prevIsLetterboxDoubleTapEnabled != mIsLetterboxDoubleTapEnabled
                 || prevLetterboxVerticalPosition != mLetterboxVerticalPosition
                 || prevLetterboxHorizontalPosition != mLetterboxHorizontalPosition
                 || prevTopActivityLetterboxWidth != mTopActivityLetterboxWidth
@@ -249,7 +249,7 @@
                     && (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION
                         || mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION));
 
-        if (mIsActivityLetterboxed && (eligibleForDisplayHorizontalEducation
+        if (mIsLetterboxDoubleTapEnabled && (eligibleForDisplayHorizontalEducation
                 || eligibleForDisplayVerticalEducation)) {
             int availableWidth = getTaskBounds().width() - mTopActivityLetterboxWidth;
             int availableHeight = getTaskBounds().height() - mTopActivityLetterboxHeight;
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 991fbaf..609e5af 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
@@ -87,6 +87,7 @@
 import com.android.wm.shell.recents.RecentTasks;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.recents.TaskStackTransitionObserver;
 import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.shared.ShellTransitions;
 import com.android.wm.shell.shared.annotations.ShellAnimationThread;
@@ -619,12 +620,13 @@
             TaskStackListenerImpl taskStackListener,
             ActivityTaskManager activityTaskManager,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+            TaskStackTransitionObserver taskStackTransitionObserver,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
         return Optional.ofNullable(
                 RecentTasksController.create(context, shellInit, shellController,
                         shellCommandHandler, taskStackListener, activityTaskManager,
-                        desktopModeTaskRepository, mainExecutor));
+                        desktopModeTaskRepository, taskStackTransitionObserver, mainExecutor));
     }
 
     @BindsOptionalOf
@@ -924,6 +926,19 @@
     }
 
     //
+    // Task Stack
+    //
+
+    @WMSingleton
+    @Provides
+    static TaskStackTransitionObserver provideTaskStackTransitionObserver(
+            Lazy<Transitions> transitions,
+            ShellInit shellInit
+    ) {
+        return new TaskStackTransitionObserver(transitions, shellInit);
+    }
+
+    //
     // Misc
     //
 
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 12bbd51..87bd840 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
@@ -121,9 +121,9 @@
  */
 @Module(
         includes = {
-            WMShellBaseModule.class,
-            PipModule.class,
-            ShellBackAnimationModule.class,
+                WMShellBaseModule.class,
+                PipModule.class,
+                ShellBackAnimationModule.class,
         })
 public abstract class WMShellModule {
 
@@ -664,7 +664,8 @@
     @Provides
     static Object provideIndependentShellComponentsToCreate(
             DragAndDropController dragAndDropController,
-            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
+            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional
+    ) {
         return new Object();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index 109868d..9192e6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -187,7 +187,10 @@
             KEYBOARD_SHORTCUT_ENTER(
                 FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER
             ),
-            SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON)
+            SCREEN_ON(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON),
+            APP_FROM_OVERVIEW(
+                FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FROM_OVERVIEW
+            ),
         }
 
         /**
@@ -204,7 +207,7 @@
                 FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT
             ),
             RETURN_HOME_OR_OVERVIEW(
-                FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME
+                FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__RETURN_HOME_OR_OVERVIEW
             ),
             TASK_FINISHED(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED),
             SCREEN_OFF(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 075e3ae..641952b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -35,15 +35,16 @@
 import androidx.core.util.putAll
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.protolog.common.ProtoLog
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
 import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.shared.TransitionUtil
@@ -80,6 +81,9 @@
     // animation was cancelled, we restore these tasks to calculate the post-Transition state
     private val tasksSavedForRecents: SparseArray<TaskInfo> = SparseArray()
 
+    // Caching whether the previous transition was exit to overview.
+    private var wasPreviousTransitionExitToOverview: Boolean = false
+
     // The instanceId for the current logging session
     private var loggerInstanceId: InstanceId? = null
 
@@ -101,7 +105,7 @@
         finishTransaction: SurfaceControl.Transaction
     ) {
         // this was a new recents animation
-        if (info.isRecentsTransition() && tasksSavedForRecents.isEmpty()) {
+        if (info.isExitToRecentsTransition() && tasksSavedForRecents.isEmpty()) {
             KtProtoLog.v(
                 WM_SHELL_DESKTOP_MODE,
                 "DesktopModeLogger: Recents animation running, saving tasks for later"
@@ -143,6 +147,7 @@
             preTransitionVisibleFreeformTasks = visibleFreeformTaskInfos,
             postTransitionVisibleFreeformTasks = postTransitionVisibleFreeformTasks
         )
+        wasPreviousTransitionExitToOverview = info.isExitToRecentsTransition()
     }
 
     override fun onTransitionStarting(transition: IBinder) {}
@@ -309,22 +314,40 @@
     }
 
     /** Get [EnterReason] for this session enter */
-    private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason {
-        return when (transitionInfo.type) {
-            WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON
-            Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP -> EnterReason.APP_HANDLE_DRAG
-            TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON -> EnterReason.APP_HANDLE_MENU_BUTTON
-            // TODO(b/344822506): Create and update EnterReason to APP_FROM_OVERVIEW
-            TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW -> EnterReason.UNKNOWN_ENTER
-            TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT -> EnterReason.KEYBOARD_SHORTCUT_ENTER
-            WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
-            else -> EnterReason.UNKNOWN_ENTER
+    private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason =
+        when {
+            transitionInfo.type == WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON
+            transitionInfo.type == Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
+                EnterReason.APP_HANDLE_DRAG
+            transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
+                EnterReason.APP_HANDLE_MENU_BUTTON
+            transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW ->
+                EnterReason.APP_FROM_OVERVIEW
+            transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT ->
+                EnterReason.KEYBOARD_SHORTCUT_ENTER
+            // NOTE: the below condition also applies for EnterReason quickswitch
+            transitionInfo.type == WindowManager.TRANSIT_TO_FRONT -> EnterReason.OVERVIEW
+            // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+            // next transition involving freeform windows.
+            // TODO(b/346564416): Modify logging for cancelled recents once it transition is
+            //  changed. Also see how to account to time difference between actual enter time and
+            //  time of this log. Also account for the missed session when exit happens just after
+            //  a cancelled recents.
+            wasPreviousTransitionExitToOverview -> EnterReason.OVERVIEW
+            transitionInfo.type == WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
+            else -> {
+                ProtoLog.w(
+                    WM_SHELL_DESKTOP_MODE,
+                    "Unknown enter reason for transition type ${transitionInfo.type}",
+                    transitionInfo.type
+                )
+                EnterReason.UNKNOWN_ENTER
+            }
         }
-    }
 
     /** Get [ExitReason] for this session exit */
-    private fun getExitReason(transitionInfo: TransitionInfo): ExitReason {
-        return when {
+    private fun getExitReason(transitionInfo: TransitionInfo): ExitReason =
+         when {
             transitionInfo.type == WindowManager.TRANSIT_SLEEP -> ExitReason.SCREEN_OFF
             transitionInfo.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED
             transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT
@@ -332,10 +355,16 @@
                 ExitReason.APP_HANDLE_MENU_BUTTON_EXIT
             transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT ->
                 ExitReason.KEYBOARD_SHORTCUT_EXIT
-            transitionInfo.isRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW
-            else -> ExitReason.UNKNOWN_EXIT
+            transitionInfo.isExitToRecentsTransition() -> ExitReason.RETURN_HOME_OR_OVERVIEW
+            else -> {
+                ProtoLog.w(
+                    WM_SHELL_DESKTOP_MODE,
+                    "Unknown exit reason for transition type ${transitionInfo.type}",
+                    transitionInfo.type
+                )
+                ExitReason.UNKNOWN_EXIT
+            }
         }
-    }
 
     /** Adds tasks to the saved copy of freeform taskId, taskInfo. Only used for testing. */
     @VisibleForTesting
@@ -358,7 +387,7 @@
         return this.windowingMode == WINDOWING_MODE_FREEFORM
     }
 
-    private fun TransitionInfo.isRecentsTransition(): Boolean {
+    private fun TransitionInfo.isExitToRecentsTransition(): Boolean {
         return this.type == WindowManager.TRANSIT_TO_FRONT &&
             this.flags == WindowManager.TRANSIT_FLAG_IS_RECENTS
     }
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 83a8d4c..c5111d6 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
@@ -54,7 +54,6 @@
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
-import com.android.wm.shell.common.ExecutorUtils
 import com.android.wm.shell.common.ExternalInterfaceBinder
 import com.android.wm.shell.common.LaunchAdjacentController
 import com.android.wm.shell.common.MultiInstanceHelper
@@ -76,7 +75,7 @@
 import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.shared.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE
-import com.android.wm.shell.shared.DesktopModeStatus.isDesktopDensityOverrideSet
+import com.android.wm.shell.shared.DesktopModeStatus.useDesktopOverrideDensity
 import com.android.wm.shell.shared.annotations.ExternalThread
 import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.splitscreen.SplitScreenController
@@ -156,6 +155,8 @@
                 visualIndicator = null
             }
         }
+    private val sysUIPackageName = context.resources.getString(
+        com.android.internal.R.string.config_systemUi)
 
     private val transitionAreaHeight
         get() =
@@ -215,6 +216,11 @@
         return visualIndicator
     }
 
+    // TODO(b/347289970): Consider replacing with API
+    private fun isSystemUIApplication(taskInfo: RunningTaskInfo): Boolean {
+        return taskInfo.baseActivity?.packageName == sysUIPackageName
+    }
+
     fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) {
         toggleResizeDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)
         enterDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)
@@ -350,6 +356,14 @@
             )
             return
         }
+        if (isSystemUIApplication(task)) {
+            KtProtoLog.w(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopTasksController: Cannot enter desktop, " +
+                        "systemUI top activity found."
+            )
+            return
+        }
         KtProtoLog.v(
             WM_SHELL_DESKTOP_MODE,
             "DesktopTasksController: moveToDesktop taskId=%d",
@@ -897,7 +911,9 @@
                 when {
                     request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
                     // Check if the task has a top transparent activity
-                    shouldLaunchAsModal(task) -> handleTransparentTaskLaunch(task)
+                    shouldLaunchAsModal(task) -> handleIncompatibleTaskLaunch(task)
+                    // Check if the task has a top systemUI activity
+                    isSystemUIApplication(task) -> handleIncompatibleTaskLaunch(task)
                     // Check if fullscreen task should be updated
                     task.isFullscreen -> handleFullscreenTaskLaunch(task, transition)
                     // Check if freeform task should be updated
@@ -931,6 +947,7 @@
             .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
     }
 
+    // TODO(b/347289970): Consider replacing with API
     private fun shouldLaunchAsModal(task: TaskInfo) =
         Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
 
@@ -961,8 +978,8 @@
             }
         }
         val wct = WindowContainerTransaction()
-        if (isDesktopDensityOverrideSet()) {
-            // TODO(344599474) reintroduce density changes behind a disabled flag
+        if (useDesktopOverrideDensity()) {
+            wct.setDensityDpi(task.token, DESKTOP_DENSITY_OVERRIDE)
         }
         // Desktop Mode is showing and we're launching a new Task - we might need to minimize
         // a Task.
@@ -997,8 +1014,11 @@
         return null
     }
 
-    // Always launch transparent tasks in fullscreen.
-    private fun handleTransparentTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
+    /**
+     * If a task is not compatible with desktop mode freeform, it should always be launched in
+     * fullscreen.
+     */
+    private fun handleIncompatibleTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
         // Already fullscreen, no-op.
         if (task.isFullscreen) return null
         return WindowContainerTransaction().also { wct -> addMoveToFullscreenChanges(wct, task) }
@@ -1036,7 +1056,7 @@
         }
         wct.setWindowingMode(taskInfo.token, targetWindowingMode)
         wct.reorder(taskInfo.token, true /* onTop */)
-        if (isDesktopDensityOverrideSet()) {
+        if (useDesktopOverrideDensity()) {
             wct.setDensityDpi(taskInfo.token, DESKTOP_DENSITY_OVERRIDE)
         }
     }
@@ -1056,7 +1076,7 @@
             }
         wct.setWindowingMode(taskInfo.token, targetWindowingMode)
         wct.setBounds(taskInfo.token, Rect())
-        if (isDesktopDensityOverrideSet()) {
+        if (useDesktopOverrideDensity()) {
             wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
         }
     }
@@ -1471,13 +1491,13 @@
         }
 
         override fun showDesktopApps(displayId: Int, remoteTransition: RemoteTransition?) {
-            ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "showDesktopApps") { c ->
+            executeRemoteCallWithTaskPermission(controller, "showDesktopApps") { c ->
                 c.showDesktopApps(displayId, remoteTransition)
             }
         }
 
         override fun showDesktopApp(taskId: Int) {
-            ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "showDesktopApp") { c ->
+            executeRemoteCallWithTaskPermission(controller, "showDesktopApp") { c ->
                 c.moveTaskToFront(taskId)
             }
         }
@@ -1495,7 +1515,7 @@
 
         override fun getVisibleTaskCount(displayId: Int): Int {
             val result = IntArray(1)
-            ExecutorUtils.executeRemoteCallWithTaskPermission(
+            executeRemoteCallWithTaskPermission(
                 controller,
                 "getVisibleTaskCount",
                 { controller -> result[0] = controller.getVisibleTaskCount(displayId) },
@@ -1505,7 +1525,7 @@
         }
 
         override fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
-            ExecutorUtils.executeRemoteCallWithTaskPermission(
+            executeRemoteCallWithTaskPermission(
                 controller,
                 "onDesktopSplitSelectAnimComplete"
             ) { c ->
@@ -1519,13 +1539,13 @@
                 "IDesktopModeImpl: set task listener=%s",
                 listener ?: "null"
             )
-            ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "setTaskListener") { _ ->
+            executeRemoteCallWithTaskPermission(controller, "setTaskListener") { _ ->
                 listener?.let { remoteListener.register(it) } ?: remoteListener.unregister()
             }
         }
 
         override fun moveToDesktop(taskId: Int, transitionSource: DesktopModeTransitionSource) {
-            ExecutorUtils.executeRemoteCallWithTaskPermission(controller, "moveToDesktop") { c ->
+            executeRemoteCallWithTaskPermission(controller, "moveToDesktop") { c ->
                 c.moveToDesktop(taskId, transitionSource = transitionSource)
             }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
index 9aa5f4f..0acc7df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
@@ -54,8 +54,8 @@
   extend `ExternalInterfaceBinder` and implement `invalidate()` to ensure it doesn't hold long
   references to the outer controller
 - Make the controller implement `RemoteCallable<T>`, and have all incoming calls use one of
-  the `ExecutorUtils.executeRemoteCallWithTaskPermission()` calls to verify the caller's identity
-  and ensure the call happens on the main shell thread and not the binder thread
+  the `executeRemoteCallWithTaskPermission()` calls to verify the caller's identity and ensure the
+  call happens on the main shell thread and not the binder thread
 - Inject `ShellController` and add the instance of the implementation as external interface
 - In Launcher, update `TouchInteractionService` to pass the interface to `SystemUIProxy`, and then
   call the SystemUIProxy method as needed in that code
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 7e70d6a..c374eb8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -32,7 +32,6 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP;
 
 import android.app.ActivityManager;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 39b9000..962309f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -19,7 +19,6 @@
 import static android.os.UserHandle.myUserId;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
 import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
 import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index eb845db..0a3c15b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -40,6 +40,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.transition.Transitions;
 
@@ -583,7 +584,7 @@
         }
 
         static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
-                Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
+                Rect baseValue, Rect startValue, Rect endValue, Rect sourceRectHint,
                 @PipAnimationController.TransitionDirection int direction, float startingAngle,
                 @Surface.Rotation int rotationDelta) {
             final boolean isOutPipDirection = isOutPipDirection(direction);
@@ -613,14 +614,25 @@
                 initialContainerRect = initialSourceValue;
             }
 
-            final Rect sourceHintRectInsets;
-            if (sourceHintRect == null) {
-                sourceHintRectInsets = null;
+            final Rect adjustedSourceRectHint = new Rect();
+            if (sourceRectHint == null || sourceRectHint.isEmpty()) {
+                // Crop a Rect matches the aspect ratio and pivots at the center point.
+                // This is done for entering case only.
+                if (isInPipDirection(direction)) {
+                    final float aspectRatio = endValue.width() / (float) endValue.height();
+                    adjustedSourceRectHint.set(PipUtils.getEnterPipWithOverlaySrcRectHint(
+                            startValue, aspectRatio));
+                }
             } else {
-                sourceHintRectInsets = new Rect(sourceHintRect.left - initialContainerRect.left,
-                        sourceHintRect.top - initialContainerRect.top,
-                        initialContainerRect.right - sourceHintRect.right,
-                        initialContainerRect.bottom - sourceHintRect.bottom);
+                adjustedSourceRectHint.set(sourceRectHint);
+            }
+            final Rect sourceHintRectInsets = new Rect();
+            if (!adjustedSourceRectHint.isEmpty()) {
+                sourceHintRectInsets.set(
+                        adjustedSourceRectHint.left - initialContainerRect.left,
+                        adjustedSourceRectHint.top - initialContainerRect.top,
+                        initialContainerRect.right - adjustedSourceRectHint.right,
+                        initialContainerRect.bottom - adjustedSourceRectHint.bottom);
             }
             final Rect zeroInsets = new Rect(0, 0, 0, 0);
 
@@ -648,7 +660,7 @@
                     }
                     float angle = (1.0f - fraction) * startingAngle;
                     setCurrentValue(bounds);
-                    if (inScaleTransition() || sourceHintRect == null) {
+                    if (inScaleTransition() || adjustedSourceRectHint.isEmpty()) {
                         if (isOutPipDirection) {
                             getSurfaceTransactionHelper().crop(tx, leash, end)
                                     .scale(tx, leash, end, bounds);
@@ -661,7 +673,7 @@
                     } else {
                         final Rect insets = computeInsets(fraction);
                         getSurfaceTransactionHelper().scaleAndCrop(tx, leash,
-                                sourceHintRect, initialSourceValue, bounds, insets,
+                                adjustedSourceRectHint, initialSourceValue, bounds, insets,
                                 isInPipDirection, fraction);
                         if (shouldApplyCornerRadius()) {
                             final Rect sourceBounds = new Rect(initialContainerRect);
@@ -729,9 +741,6 @@
                 }
 
                 private Rect computeInsets(float fraction) {
-                    if (sourceHintRectInsets == null) {
-                        return zeroInsets;
-                    }
                     final Rect startRect = isOutPipDirection ? sourceHintRectInsets : zeroInsets;
                     final Rect endRect = isOutPipDirection ? zeroInsets : sourceHintRectInsets;
                     return mInsetsEvaluator.evaluate(fraction, startRect, endRect);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
index e11e859..ff2d46e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
@@ -226,11 +226,10 @@
                     appBoundsCenterX - mOverlayHalfSize,
                     appBoundsCenterY - mOverlayHalfSize);
             // Scale back the bitmap with the pivot point at center.
-            mTmpTransform.postScale(
+            final float scale = Math.min(
                     (float) mAppBounds.width() / currentBounds.width(),
-                    (float) mAppBounds.height() / currentBounds.height(),
-                    appBoundsCenterX,
-                    appBoundsCenterY);
+                    (float) mAppBounds.height() / currentBounds.height());
+            mTmpTransform.postScale(scale, scale, appBoundsCenterX, appBoundsCenterY);
             atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
                     .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index a58d94e..202f60d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -137,7 +137,7 @@
         mTmpDestinationRect.inset(insets);
         // Scale to the bounds no smaller than the destination and offset such that the top/left
         // of the scaled inset source rect aligns with the top/left of the destination bounds
-        final float scale;
+        final float scale, left, top;
         if (isInPipDirection
                 && sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) {
             // scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only.
@@ -148,12 +148,15 @@
                     ? (float) destinationBounds.width() / sourceBounds.width()
                     : (float) destinationBounds.height() / sourceBounds.height();
             scale = (1 - fraction) * startScale + fraction * endScale;
+            left = destinationBounds.left - insets.left * scale;
+            top = destinationBounds.top - insets.top * scale;
         } else {
             scale = Math.max((float) destinationBounds.width() / sourceBounds.width(),
                     (float) destinationBounds.height() / sourceBounds.height());
+            // Work around the rounding error by fix the position at very beginning.
+            left = scale == 1 ? 0 : destinationBounds.left - insets.left * scale;
+            top = scale == 1 ? 0 : destinationBounds.top - insets.top * scale;
         }
-        final float left = destinationBounds.left - insets.left * scale;
-        final float top = destinationBounds.top - insets.top * scale;
         mTmpTransform.setScale(scale, scale);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
                 .setCrop(leash, mTmpDestinationRect)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e1657f9..e4420d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -373,6 +373,10 @@
     @NonNull
     final Rect mAppBounds = new Rect();
 
+    /** The source rect hint from stopSwipePipToHome(). */
+    @Nullable
+    private Rect mSwipeSourceRectHint;
+
     public PipTaskOrganizer(Context context,
             @NonNull SyncTransactionQueue syncTransactionQueue,
             @NonNull PipTransitionState pipTransitionState,
@@ -504,7 +508,7 @@
      * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
      */
     public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
-            SurfaceControl overlay, Rect appBounds) {
+            SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "stopSwipePipToHome: %s, stat=%s", componentName, mPipTransitionState);
         // do nothing if there is no startSwipePipToHome being called before
@@ -513,6 +517,7 @@
         }
         mPipBoundsState.setBounds(destinationBounds);
         setContentOverlay(overlay, appBounds);
+        mSwipeSourceRectHint = sourceRectHint;
         if (ENABLE_SHELL_TRANSITIONS && overlay != null) {
             // With Shell transition, the overlay was attached to the remote transition leash, which
             // will be removed when the current transition is finished, so we need to reparent it
@@ -529,6 +534,20 @@
         }
     }
 
+    /**
+     * Returns non-null Rect if the pip is entering from swipe-to-home with a specified source hint.
+     * This also consumes the rect hint.
+     */
+    @Nullable
+    Rect takeSwipeSourceRectHint() {
+        final Rect sourceRectHint = mSwipeSourceRectHint;
+        if (sourceRectHint == null || sourceRectHint.isEmpty()) {
+            return null;
+        }
+        mSwipeSourceRectHint = null;
+        return mPipTransitionState.getInSwipePipToHomeTransition() ? sourceRectHint : null;
+    }
+
     private void mayRemoveContentOverlay(SurfaceControl overlay) {
         final WeakReference<SurfaceControl> overlayRef = new WeakReference<>(overlay);
         final long timeoutDuration = (mEnterAnimationDuration
@@ -603,6 +622,8 @@
             // the end of the enter animation and reschedule exitPip to run after enter-PiP
             // has finished its transition and allowed the client to draw in PiP mode.
             mPipTransitionController.end(() -> {
+                // TODO(341627042): force set to entered state to avoid potential stack overflow.
+                mPipTransitionState.setTransitionState(PipTransitionState.ENTERED_PIP);
                 exitPip(animationDurationMs, requestEnterSplit);
             });
             return;
@@ -978,7 +999,6 @@
 
     private void onEndOfSwipePipToHomeTransition() {
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            mPipTransitionController.setEnterAnimationType(ANIM_TYPE_BOUNDS);
             return;
         }
 
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 b52b0d8..3cae72d 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
@@ -200,9 +200,6 @@
             animator.cancel();
         }
         mExitTransition = mTransitions.startTransition(type, out, this);
-        if (mPipOrganizer.getOutPipWindowingMode() == WINDOWING_MODE_UNDEFINED) {
-            mHomeTransitionObserver.notifyHomeVisibilityChanged(false /* isVisible */);
-        }
     }
 
     @Override
@@ -659,6 +656,9 @@
             startTransaction.remove(mPipOrganizer.mPipOverlay);
             mPipOrganizer.clearContentOverlay();
         }
+        if (mPipOrganizer.getOutPipWindowingMode() == WINDOWING_MODE_UNDEFINED) {
+            mHomeTransitionObserver.notifyHomeVisibilityChanged(false /* isVisible */);
+        }
         if (pipChange == null) {
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                     "%s: No window of exiting PIP is found. Can't play expand animation", TAG);
@@ -1004,8 +1004,11 @@
         final Rect currentBounds = pipChange.getStartAbsBounds();
 
         int rotationDelta = deltaRotation(startRotation, endRotation);
-        Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
-                taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
+        Rect sourceHintRect = mPipOrganizer.takeSwipeSourceRectHint();
+        if (sourceHintRect == null) {
+            sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+                    taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
+        }
         if (rotationDelta != Surface.ROTATION_0
                 && endRotation != mPipDisplayLayoutState.getRotation()) {
             // Computes the destination bounds in new rotation.
@@ -1080,6 +1083,8 @@
             mSurfaceTransactionHelper
                     .crop(finishTransaction, leash, destinationBounds)
                     .round(finishTransaction, leash, true /* applyCornerRadius */);
+            // Always reset to bounds animation type afterwards.
+            setEnterAnimationType(ANIM_TYPE_BOUNDS);
         } else {
             throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 1d1a4e2..6eefdcf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -311,6 +311,14 @@
     }
 
     /**
+     * Finish the current transition if possible.
+     *
+     * @param tx transaction to be applied with a potentially new draw after finishing.
+     */
+    public void finishTransition(@Nullable SurfaceControl.Transaction tx) {
+    }
+
+    /**
      * End the currently-playing PiP animation.
      *
      * @param onTransitionEnd callback to run upon finishing the playing transition.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 85f9194..8c4bf76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -22,7 +22,6 @@
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PIP_TRANSITION;
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
@@ -1002,9 +1001,9 @@
     }
 
     private void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
-            SurfaceControl overlay, Rect appBounds) {
+            SurfaceControl overlay, Rect appBounds, Rect sourceRectHint) {
         mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay,
-                appBounds);
+                appBounds, sourceRectHint);
     }
 
     private void abortSwipePipToHome(int taskId, ComponentName componentName) {
@@ -1292,13 +1291,15 @@
 
         @Override
         public void stopSwipePipToHome(int taskId, ComponentName componentName,
-                Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+                Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+                Rect sourceRectHint) {
             if (overlay != null) {
                 overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
             }
             executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
                     (controller) -> controller.stopSwipePipToHome(
-                            taskId, componentName, destinationBounds, overlay, appBounds));
+                            taskId, componentName, destinationBounds, overlay, appBounds,
+                            sourceRectHint));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
index 3e298e5..895c2ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
@@ -19,11 +19,13 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.content.Context;
 import android.view.SurfaceControl;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.wm.shell.R;
 import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
 
 import java.lang.annotation.Retention;
@@ -45,6 +47,7 @@
     public static final int FADE_IN = 0;
     public static final int FADE_OUT = 1;
 
+    private final int mEnterAnimationDuration;
     private final SurfaceControl mLeash;
     private final SurfaceControl.Transaction mStartTransaction;
 
@@ -55,7 +58,8 @@
     private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
 
-    public PipAlphaAnimator(SurfaceControl leash,
+    public PipAlphaAnimator(Context context,
+            SurfaceControl leash,
             SurfaceControl.Transaction tx,
             @Fade int direction) {
         mLeash = leash;
@@ -67,6 +71,9 @@
         }
         mSurfaceControlTransactionFactory =
                 new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+        mEnterAnimationDuration = context.getResources()
+                .getInteger(R.integer.config_pipEnterAnimationDuration);
+        setDuration(mEnterAnimationDuration);
         addListener(this);
         addUpdateListener(this);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
new file mode 100644
index 0000000..5c561fe
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -0,0 +1,154 @@
+/*
+ * 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.pip2.animation;
+
+import android.animation.Animator;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+
+/**
+ * Animator that handles any resize related animation for PIP.
+ */
+public class PipResizeAnimator extends ValueAnimator
+        implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener{
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private final SurfaceControl mLeash;
+    @Nullable
+    private SurfaceControl.Transaction mStartTx;
+    @Nullable
+    private SurfaceControl.Transaction mFinishTx;
+    @Nullable
+    private Runnable mAnimationStartCallback;
+    @Nullable
+    private Runnable mAnimationEndCallback;
+    private RectEvaluator mRectEvaluator;
+    private final Rect mBaseBounds = new Rect();
+    private final Rect mStartBounds = new Rect();
+    private final Rect mEndBounds = new Rect();
+    private final Rect mAnimatedRect = new Rect();
+    private final float mDelta;
+
+    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+            mSurfaceControlTransactionFactory;
+
+    public PipResizeAnimator(@NonNull Context context,
+            @NonNull SurfaceControl leash,
+            @Nullable SurfaceControl.Transaction startTransaction,
+            @Nullable SurfaceControl.Transaction finishTransaction,
+            @NonNull Rect baseBounds,
+            @NonNull Rect startBounds,
+            @NonNull Rect endBounds,
+            int duration,
+            float delta) {
+        mContext = context;
+        mLeash = leash;
+        mStartTx = startTransaction;
+        mFinishTx = finishTransaction;
+        mSurfaceControlTransactionFactory =
+                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+
+        mBaseBounds.set(baseBounds);
+        mStartBounds.set(startBounds);
+        mAnimatedRect.set(startBounds);
+        mEndBounds.set(endBounds);
+        mDelta = delta;
+
+        mRectEvaluator = new RectEvaluator(mAnimatedRect);
+
+        setObjectValues(startBounds, endBounds);
+        addListener(this);
+        addUpdateListener(this);
+        setEvaluator(mRectEvaluator);
+        // TODO: change this
+        setDuration(duration);
+    }
+
+    public void setAnimationStartCallback(@NonNull Runnable runnable) {
+        mAnimationStartCallback = runnable;
+    }
+
+    public void setAnimationEndCallback(@NonNull Runnable runnable) {
+        mAnimationEndCallback = runnable;
+    }
+
+    @Override
+    public void onAnimationStart(@NonNull Animator animation) {
+        if (mAnimationStartCallback != null) {
+            mAnimationStartCallback.run();
+        }
+        if (mStartTx != null) {
+            setBoundsAndRotation(mStartTx, mLeash, mBaseBounds, mStartBounds, mDelta);
+            mStartTx.apply();
+        }
+    }
+
+    @Override
+    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+        final float fraction = getAnimatedFraction();
+        final float degrees = (1.0f - fraction) * mDelta;
+        setBoundsAndRotation(tx, mLeash, mBaseBounds, mAnimatedRect, degrees);
+        tx.apply();
+    }
+
+    /**
+     * Set a proper transform matrix for a leash to move it to given bounds with a certain rotation.
+     *
+     * @param baseBounds crop/buffer size relative to which we are scaling the leash.
+     * @param targetBounds bounds to which we are scaling the leash.
+     * @param degrees degrees of rotation - counter-clockwise is positive by convention.
+     */
+    public static void setBoundsAndRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
+            Rect baseBounds, Rect targetBounds, float degrees) {
+        Matrix transformTensor = new Matrix();
+        final float[] mMatrixTmp = new float[9];
+        final float scale = (float) targetBounds.width() / baseBounds.width();
+
+        transformTensor.setScale(scale, scale);
+        transformTensor.postTranslate(targetBounds.left, targetBounds.top);
+        transformTensor.postRotate(degrees, targetBounds.centerX(), targetBounds.centerY());
+
+        tx.setMatrix(leash, transformTensor, mMatrixTmp);
+    }
+
+    @Override
+    public void onAnimationEnd(@NonNull Animator animation) {
+        if (mFinishTx != null) {
+            setBoundsAndRotation(mFinishTx, mLeash, mBaseBounds, mEndBounds, 0f);
+        }
+        if (mAnimationEndCallback != null) {
+            mAnimationEndCallback.run();
+        }
+    }
+
+    @Override
+    public void onAnimationCancel(@NonNull Animator animation) {}
+
+    @Override
+    public void onAnimationRepeat(@NonNull Animator animation) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index f5afeea..fc0d36d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
 
 import android.app.ActivityManager;
@@ -286,7 +285,8 @@
     }
 
     private void onSwipePipToHomeAnimationStart(int taskId, ComponentName componentName,
-            Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+            Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+            Rect sourceRectHint) {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "onSwipePipToHomeAnimationStart: %s", componentName);
         Bundle extra = new Bundle();
@@ -391,6 +391,7 @@
         @Override
         public void invalidate() {
             mController = null;
+            // Unregister the listener to ensure any registered binder death recipients are unlinked
             mListener.unregister();
         }
 
@@ -409,13 +410,15 @@
 
         @Override
         public void stopSwipePipToHome(int taskId, ComponentName componentName,
-                Rect destinationBounds, SurfaceControl overlay, Rect appBounds) {
+                Rect destinationBounds, SurfaceControl overlay, Rect appBounds,
+                Rect sourceRectHint) {
             if (overlay != null) {
                 overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
             }
             executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
                     (controller) -> controller.onSwipePipToHomeAnimationStart(
-                            taskId, componentName, destinationBounds, overlay, appBounds));
+                            taskId, componentName, destinationBounds, overlay, appBounds,
+                            sourceRectHint));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index aed493f..495cd00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -731,8 +731,8 @@
                 settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */);
                 cleanUpHighPerfSessionMaybe();
 
-                // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core.
-                mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
+                // Signal that the transition is done - should update transition state by default.
+                mPipScheduler.scheduleFinishResizePip(false /* configAtEnd */);
                 break;
             case PipTransitionState.EXITING_PIP:
                 // We need to force finish any local animators if about to leave PiP, to avoid
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 7dffe54..33e80bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -38,6 +38,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.util.Preconditions;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -45,6 +46,7 @@
 import com.android.wm.shell.common.pip.PipPerfHintController;
 import com.android.wm.shell.common.pip.PipPinchResizingAlgorithm;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.pip2.animation.PipResizeAnimator;
 
 import java.io.PrintWriter;
 import java.util.function.Consumer;
@@ -82,6 +84,7 @@
     private final Rect mLastResizeBounds = new Rect();
     private final Rect mUserResizeBounds = new Rect();
     private final Rect mDownBounds = new Rect();
+    private final Rect mStartBoundsAfterRelease = new Rect();
     private final Runnable mUpdateMovementBoundsRunnable;
     private final Consumer<Rect> mUpdateResizeBoundsCallback;
 
@@ -418,7 +421,9 @@
         if (!mOngoingPinchToResize) {
             return;
         }
-        final Rect startBounds = new Rect(mLastResizeBounds);
+
+        // Cache initial bounds after release for animation before mLastResizeBounds are modified.
+        mStartBoundsAfterRelease.set(mLastResizeBounds);
 
         // If user resize is pretty close to max size, just auto resize to max.
         if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
@@ -527,28 +532,39 @@
                     int offsetY = inTopHalf ? 1 : -1;
                     mLastResizeBounds.offset(0 /* dx */, offsetY);
                 }
-
                 mWaitingForBoundsChangeTransition = true;
-                mPipScheduler.scheduleAnimateResizePip(mLastResizeBounds);
+
+                // Schedule PiP resize transition, but delay any config updates until very end.
+                mPipScheduler.scheduleAnimateResizePip(mLastResizeBounds, true /* configAtEnd */);
                 break;
             case PipTransitionState.CHANGING_PIP_BOUNDS:
                 if (!mWaitingForBoundsChangeTransition) break;
-
-                // If bounds change transition was scheduled from this class, handle leash updates.
+                // If resize transition was scheduled from this component, handle leash updates.
                 mWaitingForBoundsChangeTransition = false;
 
+                SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+                Preconditions.checkState(pipLeash != null,
+                        "No leash cached by mPipTransitionState=" + mPipTransitionState);
+
                 SurfaceControl.Transaction startTx = extra.getParcelable(
                         PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
-                Rect destinationBounds = extra.getParcelable(
-                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
-                startTx.apply();
+                SurfaceControl.Transaction finishTx = extra.getParcelable(
+                        PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class);
+                startTx.setWindowCrop(pipLeash, mPipBoundsState.getBounds().width(),
+                        mPipBoundsState.getBounds().height());
 
-                // All motion operations have actually finished, so make bounds cache updates.
-                mUpdateResizeBoundsCallback.accept(destinationBounds);
-                cleanUpHighPerfSessionMaybe();
+                PipResizeAnimator animator = new PipResizeAnimator(mContext, pipLeash,
+                        startTx, finishTx, mPipBoundsState.getBounds(), mStartBoundsAfterRelease,
+                        mLastResizeBounds, PINCH_RESIZE_SNAP_DURATION, mAngle);
+                animator.setAnimationEndCallback(() -> {
+                    // All motion operations have actually finished, so make bounds cache updates.
+                    mUpdateResizeBoundsCallback.accept(mLastResizeBounds);
+                    cleanUpHighPerfSessionMaybe();
 
-                // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core.
-                mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
+                    // Signal that we are done with resize transition
+                    mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */);
+                });
+                animator.start();
                 break;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 4947507..9c1e321 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -153,15 +153,46 @@
      * Animates resizing of the pinned stack given the duration.
      */
     public void scheduleAnimateResizePip(Rect toBounds) {
+        scheduleAnimateResizePip(toBounds, false /* configAtEnd */);
+    }
+
+    /**
+     * Animates resizing of the pinned stack given the duration.
+     *
+     * @param configAtEnd true if we are delaying config updates until the transition ends.
+     */
+    public void scheduleAnimateResizePip(Rect toBounds, boolean configAtEnd) {
         if (mPipTransitionState.mPipTaskToken == null || !mPipTransitionState.isInPip()) {
             return;
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
+        if (configAtEnd) {
+            wct.deferConfigToTransitionEnd(mPipTransitionState.mPipTaskToken);
+        }
         mPipTransitionController.startResizeTransition(wct);
     }
 
     /**
+     * Signals to Core to finish the PiP resize transition.
+     * Note that we do not allow any actual WM Core changes at this point.
+     *
+     * @param configAtEnd true if we are waiting for config updates at the end of the transition.
+     */
+    public void scheduleFinishResizePip(boolean configAtEnd) {
+        SurfaceControl.Transaction tx = null;
+        if (configAtEnd) {
+            tx = new SurfaceControl.Transaction();
+            tx.addTransactionCommittedListener(mMainExecutor, () -> {
+                mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
+            });
+        } else {
+            mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
+        }
+        mPipTransitionController.finishTransition(tx);
+    }
+
+    /**
      * Directly perform a scaled matrix transformation on the leash. This will not perform any
      * {@link WindowContainerTransaction}.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 3e215d9..57dc5f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -293,37 +293,32 @@
             return false;
         }
 
+        SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
         PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams;
-        Rect srcRectHint = params.getSourceRectHint();
-        Rect startBounds = pipChange.getStartAbsBounds();
+
+        Rect appBounds = mPipTransitionState.getSwipePipToHomeAppBounds();
         Rect destinationBounds = pipChange.getEndAbsBounds();
 
+        float aspectRatio = pipChange.getTaskInfo().pictureInPictureParams.getAspectRatioFloat();
+
+        // We fake the source rect hint when the one prvided by the app is invalid for
+        // the animation with an app icon overlay.
+        Rect animationSrcRectHint = overlayLeash == null ? params.getSourceRectHint()
+                : PipUtils.getEnterPipWithOverlaySrcRectHint(appBounds, aspectRatio);
+
         WindowContainerTransaction finishWct = new WindowContainerTransaction();
         SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
 
-        if (PipBoundsAlgorithm.isSourceRectHintValidForEnterPip(srcRectHint, destinationBounds)) {
-            final float scale = (float) destinationBounds.width() / srcRectHint.width();
-            startTransaction.setWindowCrop(pipLeash, srcRectHint);
-            startTransaction.setPosition(pipLeash,
-                    destinationBounds.left - srcRectHint.left * scale,
-                    destinationBounds.top - srcRectHint.top * scale);
+        final float scale = (float) destinationBounds.width() / animationSrcRectHint.width();
+        startTransaction.setWindowCrop(pipLeash, animationSrcRectHint);
+        startTransaction.setPosition(pipLeash,
+                destinationBounds.left - animationSrcRectHint.left * scale,
+                destinationBounds.top - animationSrcRectHint.top * scale);
+        startTransaction.setScale(pipLeash, scale, scale);
 
-            // Reset the scale in case we are in the multi-activity case.
-            // TO_FRONT transition already scales down the task in single-activity case, but
-            // in multi-activity case, reparenting yields new reset scales coming from pinned task.
-            startTransaction.setScale(pipLeash, scale, scale);
-        } else {
-            final float scaleX = (float) destinationBounds.width() / startBounds.width();
-            final float scaleY = (float) destinationBounds.height() / startBounds.height();
+        if (overlayLeash != null) {
             final int overlaySize = PipContentOverlay.PipAppIconOverlay.getOverlaySize(
                     mPipTransitionState.getSwipePipToHomeAppBounds(), destinationBounds);
-            SurfaceControl overlayLeash = mPipTransitionState.getSwipePipToHomeOverlay();
-
-            startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
-                    .setScale(pipLeash, scaleX, scaleY)
-                    .setWindowCrop(pipLeash, startBounds)
-                    .reparent(overlayLeash, pipLeash)
-                    .setLayer(overlayLeash, Integer.MAX_VALUE);
 
             // Overlay needs to be adjusted once a new draw comes in resetting surface transform.
             tx.setScale(overlayLeash, 1f, 1f);
@@ -390,15 +385,23 @@
         if (pipChange == null) {
             return false;
         }
-        // cache the PiP task token and leash
-        WindowContainerToken pipTaskToken = pipChange.getContainer();
 
-        Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition.");
-        // start transition with 0 alpha
-        startTransaction.setAlpha(mPipLeash, 0f);
-        PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash,
-                startTransaction, PipAlphaAnimator.FADE_IN);
-        animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null));
+        Rect destinationBounds = pipChange.getEndAbsBounds();
+        SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
+        Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");
+
+        // Start transition with 0 alpha at the entry bounds.
+        startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
+                .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height())
+                .setAlpha(pipLeash, 0f);
+
+        PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
+                PipAlphaAnimator.FADE_IN);
+        animator.setAnimationEndCallback(() -> {
+            finishCallback.onTransitionFinished(null);
+            // This should update the pip transition state accordingly after we stop playing.
+            onClientDrawAtTransitionEnd();
+        });
 
         animator.start();
         return true;
@@ -480,10 +483,10 @@
 
     private boolean isLegacyEnter(@NonNull TransitionInfo info) {
         TransitionInfo.Change pipChange = getPipChange(info);
-        // If the only change in the changes list is a TO_FRONT mode PiP task,
+        // If the only change in the changes list is a opening type PiP task,
         // then this is legacy-enter PiP.
-        return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT
-                && info.getChanges().size() == 1;
+        return pipChange != null && info.getChanges().size() == 1
+                && (pipChange.getMode() == TRANSIT_TO_FRONT || pipChange.getMode() == TRANSIT_OPEN);
     }
 
     private boolean isRemovePipTransition(@NonNull TransitionInfo info) {
@@ -521,6 +524,20 @@
     }
 
     @Override
+    public void finishTransition(@Nullable SurfaceControl.Transaction tx) {
+        WindowContainerTransaction wct = null;
+        if (tx != null && mPipTransitionState.mPipTaskToken != null) {
+            // Outside callers can only provide a transaction to be applied with the final draw.
+            // So no actual WM changes can be applied for this transition after this point.
+            wct = new WindowContainerTransaction();
+            wct.setBoundsChangeTransaction(mPipTransitionState.mPipTaskToken, tx);
+        }
+        if (mFinishCallback != null) {
+            mFinishCallback.onTransitionFinished(wct);
+        }
+    }
+
+    @Override
     public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
             @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
         switch (newState) {
@@ -542,15 +559,6 @@
                 mPipTransitionState.mPipTaskToken = null;
                 mPipTransitionState.mPinnedTaskLeash = null;
                 break;
-            case PipTransitionState.CHANGED_PIP_BOUNDS:
-                // Note: this might not be the end of the animation, rather animator just finished
-                // adjusting startTx and finishTx and is ready to finishTransition(). The animator
-                // can still continue playing the leash into the destination bounds after.
-                if (mFinishCallback != null) {
-                    mFinishCallback.onTransitionFinished(null);
-                    mFinishCallback = null;
-                }
-                break;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
index 62d195e..245829e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl
@@ -42,4 +42,7 @@
      * Called when a running task changes.
      */
     void onRunningTaskChanged(in RunningTaskInfo taskInfo);
-}
+
+    /** A task has moved to front. */
+    oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo);
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS
new file mode 100644
index 0000000..452644b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS
@@ -0,0 +1,6 @@
+# WM shell sub-module task stack owners
+uysalorhan@google.com
+samcackett@google.com
+alexchau@google.com
+silvajordan@google.com
+uwaisashraf@google.com
\ No newline at end of file
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 863202d..03c8cf8 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
@@ -20,7 +20,7 @@
 import static android.content.pm.PackageManager.FEATURE_PC;
 
 import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+import static com.android.window.flags.Flags.enableTaskStackObserverInShell;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
 
 import android.app.ActivityManager;
@@ -58,6 +58,7 @@
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.util.GroupedRecentTaskInfo;
 import com.android.wm.shell.util.SplitBounds;
 
@@ -74,7 +75,8 @@
  * Manages the recent task list from the system, caching it as necessary.
  */
 public class RecentTasksController implements TaskStackListenerCallback,
-        RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener {
+        RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener,
+        TaskStackTransitionObserver.TaskStackTransitionObserverListener {
     private static final String TAG = RecentTasksController.class.getSimpleName();
 
     private final Context mContext;
@@ -85,6 +87,7 @@
     private final TaskStackListenerImpl mTaskStackListener;
     private final RecentTasksImpl mImpl = new RecentTasksImpl();
     private final ActivityTaskManager mActivityTaskManager;
+    private final TaskStackTransitionObserver mTaskStackTransitionObserver;
     private RecentsTransitionHandler mTransitionHandler = null;
     private IRecentTasksListener mListener;
     private final boolean mPcFeatureEnabled;
@@ -113,13 +116,15 @@
             TaskStackListenerImpl taskStackListener,
             ActivityTaskManager activityTaskManager,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+            TaskStackTransitionObserver taskStackTransitionObserver,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
         if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
             return null;
         }
         return new RecentTasksController(context, shellInit, shellController, shellCommandHandler,
-                taskStackListener, activityTaskManager, desktopModeTaskRepository, mainExecutor);
+                taskStackListener, activityTaskManager, desktopModeTaskRepository,
+                taskStackTransitionObserver, mainExecutor);
     }
 
     RecentTasksController(Context context,
@@ -129,6 +134,7 @@
             TaskStackListenerImpl taskStackListener,
             ActivityTaskManager activityTaskManager,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+            TaskStackTransitionObserver taskStackTransitionObserver,
             ShellExecutor mainExecutor) {
         mContext = context;
         mShellController = shellController;
@@ -137,6 +143,7 @@
         mPcFeatureEnabled = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
         mTaskStackListener = taskStackListener;
         mDesktopModeTaskRepository = desktopModeTaskRepository;
+        mTaskStackTransitionObserver = taskStackTransitionObserver;
         mMainExecutor = mainExecutor;
         shellInit.addInitCallback(this::onInit, this);
     }
@@ -155,6 +162,10 @@
         mShellCommandHandler.addDumpCallback(this::dump, this);
         mTaskStackListener.addListener(this);
         mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTaskListener(this));
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
+                    mMainExecutor);
+        }
     }
 
     void setTransitionHandler(RecentsTransitionHandler handler) {
@@ -268,6 +279,12 @@
         notifyRecentTasksChanged();
     }
 
+    @Override
+    public void onTaskMovedToFrontThroughTransition(
+            ActivityManager.RunningTaskInfo runningTaskInfo) {
+        notifyTaskMovedToFront(runningTaskInfo);
+    }
+
     @VisibleForTesting
     void notifyRecentTasksChanged() {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed");
@@ -329,6 +346,19 @@
         }
     }
 
+    private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+        if (mListener == null
+                || !enableTaskStackObserverInShell()
+                || taskInfo.realActivity == null) {
+            return;
+        }
+        try {
+            mListener.onTaskMovedToFront(taskInfo);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed call onTaskMovedToFront", e);
+        }
+    }
+
     private boolean shouldEnableRunningTasksForDesktopMode() {
         return mPcFeatureEnabled
                 || (DesktopModeStatus.canEnterDesktopMode(mContext)
@@ -465,6 +495,7 @@
         }
         return null;
     }
+
     public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
@@ -548,6 +579,11 @@
             public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
                 mListener.call(l -> l.onRunningTaskChanged(taskInfo));
             }
+
+            @Override
+            public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+                mListener.call(l -> l.onTaskMovedToFront(taskInfo));
+            }
         };
 
         public IRecentTasksImpl(RecentTasksController controller) {
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
new file mode 100644
index 0000000..7c5f10a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.recents
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.IBinder
+import android.util.ArrayMap
+import android.view.SurfaceControl
+import android.view.WindowManager
+import android.window.TransitionInfo
+import com.android.window.flags.Flags.enableTaskStackObserverInShell
+import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import dagger.Lazy
+import java.util.concurrent.Executor
+
+/**
+ * A [Transitions.TransitionObserver] that observes shell transitions and sends updates to listeners
+ * about task stack changes.
+ *
+ * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it
+ */
+class TaskStackTransitionObserver(
+    private val transitions: Lazy<Transitions>,
+    shellInit: ShellInit
+) : Transitions.TransitionObserver {
+
+    private val transitionToTransitionChanges: MutableMap<IBinder, TransitionChanges> =
+        mutableMapOf()
+    private val taskStackTransitionObserverListeners =
+        ArrayMap<TaskStackTransitionObserverListener, Executor>()
+
+    init {
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            shellInit.addInitCallback(::onInit, this)
+        }
+    }
+
+    fun onInit() {
+        transitions.get().registerObserver(this)
+    }
+
+    override fun onTransitionReady(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction
+    ) {
+        if (enableTaskStackObserverInShell()) {
+            val taskInfoList = mutableListOf<RunningTaskInfo>()
+            val transitionTypeList = mutableListOf<Int>()
+
+            for (change in info.changes) {
+                if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) {
+                    continue
+                }
+
+                val taskInfo = change.taskInfo
+                if (taskInfo == null || taskInfo.taskId == -1) {
+                    continue
+                }
+
+                if (change.mode == WindowManager.TRANSIT_OPEN) {
+                    change.taskInfo?.let { taskInfoList.add(it) }
+                    transitionTypeList.add(change.mode)
+                }
+            }
+            transitionToTransitionChanges.put(
+                transition,
+                TransitionChanges(taskInfoList, transitionTypeList)
+            )
+        }
+    }
+
+    override fun onTransitionStarting(transition: IBinder) {}
+
+    override fun onTransitionMerged(merged: IBinder, playing: IBinder) {}
+
+    override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+        val taskInfoList =
+            transitionToTransitionChanges.getOrDefault(transition, TransitionChanges()).taskInfoList
+        val typeList =
+            transitionToTransitionChanges
+                .getOrDefault(transition, TransitionChanges())
+                .transitionTypeList
+        transitionToTransitionChanges.remove(transition)
+
+        for ((index, taskInfo) in taskInfoList.withIndex()) {
+            if (
+                TransitionUtil.isOpeningType(typeList[index]) &&
+                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
+            ) {
+                notifyTaskStackTransitionObserverListeners(taskInfo)
+            }
+        }
+    }
+
+    fun addTaskStackTransitionObserverListener(
+        taskStackTransitionObserverListener: TaskStackTransitionObserverListener,
+        executor: Executor
+    ) {
+        taskStackTransitionObserverListeners[taskStackTransitionObserverListener] = executor
+    }
+
+    fun removeTaskStackTransitionObserverListener(
+        taskStackTransitionObserverListener: TaskStackTransitionObserverListener
+    ) {
+        taskStackTransitionObserverListeners.remove(taskStackTransitionObserverListener)
+    }
+
+    private fun notifyTaskStackTransitionObserverListeners(taskInfo: RunningTaskInfo) {
+        taskStackTransitionObserverListeners.forEach { (listener, executor) ->
+            executor.execute { listener.onTaskMovedToFrontThroughTransition(taskInfo) }
+        }
+    }
+
+    /** Listener to use to get updates regarding task stack from this observer */
+    interface TaskStackTransitionObserverListener {
+        /** Called when a task is moved to front. */
+        fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {}
+    }
+
+    private data class TransitionChanges(
+        val taskInfoList: MutableList<RunningTaskInfo> = ArrayList(),
+        val transitionTypeList: MutableList<Int> = ArrayList()
+    )
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index b9d70e1..dd219d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -24,7 +24,6 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.common.MultiInstanceHelper.getComponent;
 import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent;
 import static com.android.wm.shell.common.MultiInstanceHelper.samePackage;
@@ -1241,8 +1240,9 @@
         @Override
         public void invalidate() {
             mController = null;
-            // Unregister the listener to ensure any registered binder death recipients are unlinked
+            // Unregister the listeners to ensure any binder death recipients are unlinked
             mListener.unregister();
+            mSelectListener.unregister();
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index cc995ea..b6a18e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -1524,6 +1524,7 @@
                 prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
                 mSplitTransitions.startDismissTransition(wct, this,
                         mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED);
+                setSplitsVisible(false);
             } else {
                 exitSplitScreen(
                         mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
@@ -1893,6 +1894,10 @@
         // will be canceled.
         options.setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
         options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+
+        // TODO (b/336477473): Disallow enter PiP when launching a task in split by default;
+        //                     this might have to be changed as more split-to-pip cujs are defined.
+        options.setDisallowEnterPictureInPictureWhileLaunching(true);
         opts.putAll(options.toBundle());
     }
 
@@ -2757,6 +2762,14 @@
             // cases above and it is not already visible
             return null;
         } else {
+            if (triggerTask.parentTaskId == mMainStage.mRootTaskInfo.taskId
+                    || triggerTask.parentTaskId == mSideStage.mRootTaskInfo.taskId) {
+                ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d "
+                                + "restoring to split", request.getDebugId());
+                out = new WindowContainerTransaction();
+                mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
+                        TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, false /* resizeAnim */);
+            }
             if (isOpening && getStageOfTask(triggerTask) != null) {
                 ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split",
                         request.getDebugId());
@@ -3103,7 +3116,7 @@
                 // Includes TRANSIT_CHANGE to cover reparenting top-most task to split.
                 mainChild = change;
             } else if (sideChild == null && stageType == STAGE_TYPE_SIDE
-                    && isOpeningType(change.getMode())) {
+                    && (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) {
                 sideChild = change;
             } else if (stageType != STAGE_TYPE_UNDEFINED && change.getMode() == TRANSIT_TO_BACK) {
                 // Collect all to back task's and evict them when transition finished.
@@ -3114,7 +3127,8 @@
         SplitScreenTransitions.EnterSession pendingEnter = mSplitTransitions.mPendingEnter;
         if (pendingEnter.mExtraTransitType
                 == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
-            // Open to side should only be used when split already active and foregorund.
+            // Open to side should only be used when split already active and foregorund or when
+            // app is restoring to split from fullscreen.
             if (mainChild == null && sideChild == null) {
                 Log.w(TAG, splitFailureMessage("startPendingEnterAnimation",
                         "Launched a task in split, but didn't receive any task in transition."));
@@ -3201,6 +3215,22 @@
             mPausingTasks.clear();
         });
 
+        if (info.getType() == TRANSIT_CHANGE && !isSplitActive()
+                && pendingEnter.mExtraTransitType == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
+            if (finalMainChild != null && finalSideChild == null) {
+                requestEnterSplitSelect(finalMainChild.getTaskInfo(),
+                        new WindowContainerTransaction(),
+                        getMainStagePosition(), finalMainChild.getStartAbsBounds());
+            } else if (finalSideChild != null && finalMainChild == null) {
+                requestEnterSplitSelect(finalSideChild.getTaskInfo(),
+                        new WindowContainerTransaction(),
+                        getSideStagePosition(), finalSideChild.getStartAbsBounds());
+            } else {
+                throw new IllegalStateException(
+                        "Attempting to restore to split but reparenting change not found");
+            }
+        }
+
         finishEnterSplitScreen(finishT);
         addDividerBarToTransition(info, true /* show */);
         return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index bec4ba3..fa084c58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -23,7 +23,6 @@
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS;
 
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
 
 import android.app.ActivityManager.RunningTaskInfo;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index b1a1e59..9b27e41 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -133,5 +133,9 @@
      */
     public void invalidate(Transitions transitions) {
         transitions.unregisterObserver(this);
+        if (mListener != null) {
+            // Unregister the listener to ensure any registered binder death recipients are unlinked
+            mListener.unregister();
+        }
     }
 }
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 b056c18..f257e20 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
@@ -35,7 +35,6 @@
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
 import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -54,6 +53,7 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 import android.view.SurfaceControl;
@@ -227,7 +227,8 @@
     private boolean mDisableForceSync = false;
 
     private static final class ActiveTransition {
-        IBinder mToken;
+        final IBinder mToken;
+
         TransitionHandler mHandler;
         boolean mAborted;
         TransitionInfo mInfo;
@@ -237,6 +238,10 @@
         /** Ordered list of transitions which have been merged into this one. */
         private ArrayList<ActiveTransition> mMerged;
 
+        ActiveTransition(IBinder token) {
+            mToken = token;
+        }
+
         boolean isSync() {
             return (mInfo.getFlags() & TransitionInfo.FLAG_SYNC) != 0;
         }
@@ -266,6 +271,9 @@
         }
     }
 
+    /** All transitions that we have created, but not yet finished. */
+    private final ArrayMap<IBinder, ActiveTransition> mKnownTransitions = new ArrayMap<>();
+
     /** Keeps track of transitions which have been started, but aren't ready yet. */
     private final ArrayList<ActiveTransition> mPendingTransitions = new ArrayList<>();
 
@@ -690,7 +698,7 @@
                 info.getDebugId(), transitionToken, info);
         int activeIdx = findByToken(mPendingTransitions, transitionToken);
         if (activeIdx < 0) {
-            final ActiveTransition existing = getKnownTransition(transitionToken);
+            final ActiveTransition existing = mKnownTransitions.get(transitionToken);
             if (existing != null) {
                 Log.e(TAG, "Got duplicate transitionReady for " + transitionToken);
                 // The transition is already somewhere else in the pipeline, so just return here.
@@ -705,8 +713,8 @@
                     + transitionToken + ". expecting one of "
                     + Arrays.toString(mPendingTransitions.stream().map(
                             activeTransition -> activeTransition.mToken).toArray()));
-            final ActiveTransition fallback = new ActiveTransition();
-            fallback.mToken = transitionToken;
+            final ActiveTransition fallback = new ActiveTransition(transitionToken);
+            mKnownTransitions.put(transitionToken, fallback);
             mPendingTransitions.add(fallback);
             activeIdx = mPendingTransitions.size() - 1;
         }
@@ -746,7 +754,7 @@
                 // Sleep starts a process of forcing all prior transitions to finish immediately
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                         "Start finish-for-sync track %d", i);
-                finishForSync(active, i, null /* forceFinish */);
+                finishForSync(active.mToken, i, null /* forceFinish */);
             }
             if (hadPreceding) {
                 return false;
@@ -864,6 +872,7 @@
                     } else if (mPendingTransitions.isEmpty()) {
                         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition "
                                 + "animations finished");
+                        mKnownTransitions.clear();
                         // Run all runnables from the run-when-idle queue.
                         for (int i = 0; i < mRunWhenIdleQueue.size(); i++) {
                             mRunWhenIdleQueue.get(i).run();
@@ -884,7 +893,7 @@
                     ready.mStartT.apply();
                 }
                 // finish now since there's nothing to animate. Calls back into processReadyQueue
-                onFinish(ready, null);
+                onFinish(ready.mToken, null);
                 return;
             }
             playTransition(ready);
@@ -943,8 +952,10 @@
 
     private void playTransition(@NonNull ActiveTransition active) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
+        final var token = active.mToken;
+
         for (int i = 0; i < mObservers.size(); ++i) {
-            mObservers.get(i).onTransitionStarting(active.mToken);
+            mObservers.get(i).onTransitionStarting(token);
         }
 
         setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
@@ -953,8 +964,8 @@
         if (active.mHandler != null) {
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
                     active.mHandler);
-            boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo,
-                    active.mStartT, active.mFinishT, (wct) -> onFinish(active, wct));
+            boolean consumed = active.mHandler.startAnimation(token, active.mInfo,
+                    active.mStartT, active.mFinishT, (wct) -> onFinish(token, wct));
             if (consumed) {
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
                 mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
@@ -962,8 +973,8 @@
             }
         }
         // Otherwise give every other handler a chance
-        active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT,
-                active.mFinishT, (wct) -> onFinish(active, wct), active.mHandler);
+        active.mHandler = dispatchTransition(token, active.mInfo, active.mStartT,
+                active.mFinishT, (wct) -> onFinish(token, wct), active.mHandler);
     }
 
     /**
@@ -1039,10 +1050,15 @@
         info.releaseAnimSurfaces();
     }
 
-    private void onFinish(ActiveTransition active,
+    private void onFinish(IBinder token,
             @Nullable WindowContainerTransaction wct) {
+        final ActiveTransition active = mKnownTransitions.get(token);
+        if (active == null) {
+            Log.e(TAG, "Trying to finish a non-existent transition: " + token);
+            return;
+        }
         final Track track = mTracks.get(active.getTrack());
-        if (track.mActiveTransition != active) {
+        if (track == null || track.mActiveTransition != active) {
             Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
                     + " a handler didn't properly deal with a merge. " + active,
                     new RuntimeException());
@@ -1095,54 +1111,25 @@
                 ActiveTransition merged = active.mMerged.get(iM);
                 mOrganizer.finishTransition(merged.mToken, null /* wct */);
                 releaseSurfaces(merged.mInfo);
+                mKnownTransitions.remove(merged.mToken);
             }
             active.mMerged.clear();
         }
+        mKnownTransitions.remove(token);
 
         // Now that this is done, check the ready queue for more work.
         processReadyQueue(track);
     }
 
-    /**
-     * Checks to see if the transition specified by `token` is already known. If so, it will be
-     * returned.
-     */
-    @Nullable
-    private ActiveTransition getKnownTransition(IBinder token) {
-        for (int i = 0; i < mPendingTransitions.size(); ++i) {
-            final ActiveTransition active = mPendingTransitions.get(i);
-            if (active.mToken == token) return active;
-        }
-        for (int i = 0; i < mReadyDuringSync.size(); ++i) {
-            final ActiveTransition active = mReadyDuringSync.get(i);
-            if (active.mToken == token) return active;
-        }
-        for (int t = 0; t < mTracks.size(); ++t) {
-            final Track tr = mTracks.get(t);
-            for (int i = 0; i < tr.mReadyTransitions.size(); ++i) {
-                final ActiveTransition active = tr.mReadyTransitions.get(i);
-                if (active.mToken == token) return active;
-            }
-            final ActiveTransition active = tr.mActiveTransition;
-            if (active == null) continue;
-            if (active.mToken == token) return active;
-            if (active.mMerged == null) continue;
-            for (int m = 0; m < active.mMerged.size(); ++m) {
-                final ActiveTransition merged = active.mMerged.get(m);
-                if (merged.mToken == token) return merged;
-            }
-        }
-        return null;
-    }
-
     void requestStartTransition(@NonNull IBinder transitionToken,
             @Nullable TransitionRequestInfo request) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
                 request.getDebugId(), transitionToken, request);
-        if (getKnownTransition(transitionToken) != null) {
+        if (mKnownTransitions.containsKey(transitionToken)) {
             throw new RuntimeException("Transition already started " + transitionToken);
         }
-        final ActiveTransition active = new ActiveTransition();
+        final ActiveTransition active = new ActiveTransition(transitionToken);
+        mKnownTransitions.put(transitionToken, active);
         WindowContainerTransaction wct = null;
 
         // If we have sleep, we use a special handler and we try to finish everything ASAP.
@@ -1182,7 +1169,6 @@
             wct.setBounds(request.getTriggerTask().token, null);
         }
         mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
-        active.mToken = transitionToken;
         // Currently, WMCore only does one transition at a time. If it makes a requestStart, it
         // is already collecting that transition on core-side, so it will be the next one to
         // become ready. There may already be pending transitions added as part of direct
@@ -1201,9 +1187,10 @@
             @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
                 + "type=%d wct=%s handler=%s", type, wct, handler);
-        final ActiveTransition active = new ActiveTransition();
+        final ActiveTransition active =
+                new ActiveTransition(mOrganizer.startNewTransition(type, wct));
         active.mHandler = handler;
-        active.mToken = mOrganizer.startNewTransition(type, wct);
+        mKnownTransitions.put(active.mToken, active);
         mPendingTransitions.add(active);
         return active.mToken;
     }
@@ -1243,14 +1230,14 @@
      *
      * This is then repeated until there are no more pending sleep transitions.
      *
-     * @param reason The SLEEP transition that triggered this round of finishes. We will continue
-     *               looping round finishing transitions as long as this is still waiting.
+     * @param reason The token for the SLEEP transition that triggered this round of finishes.
+     *               We will continue looping round finishing transitions until this is ready.
      * @param forceFinish When non-null, this is the transition that we last sent the SLEEP merge
      *                    signal to -- so it will be force-finished if it's still running.
      */
-    private void finishForSync(ActiveTransition reason,
+    private void finishForSync(IBinder reason,
             int trackIdx, @Nullable ActiveTransition forceFinish) {
-        if (getKnownTransition(reason.mToken) == null) {
+        if (!mKnownTransitions.containsKey(reason)) {
             Log.d(TAG, "finishForSleep: already played sync transition " + reason);
             return;
         }
@@ -1270,7 +1257,7 @@
                     forceFinish.mHandler.onTransitionConsumed(
                             forceFinish.mToken, true /* aborted */, null /* finishTransaction */);
                 }
-                onFinish(forceFinish, null);
+                onFinish(forceFinish.mToken, null);
             }
         }
         if (track.isIdle() || mReadyDuringSync.isEmpty()) {
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 eeb3662..21b6db2 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
@@ -103,6 +103,7 @@
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Supplier;
 
@@ -152,6 +153,7 @@
     private final DisplayInsetsController mDisplayInsetsController;
     private final Region mExclusionRegion = Region.obtain();
     private boolean mInImmersiveMode;
+    private final String mSysUIPackageName;
 
     private final ISystemGestureExclusionListener mGestureExclusionListener =
             new ISystemGestureExclusionListener.Stub() {
@@ -247,6 +249,8 @@
         mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
         mInputManager = mContext.getSystemService(InputManager.class);
         mWindowDecorByTaskId = windowDecorByTaskId;
+        mSysUIPackageName = mContext.getResources().getString(
+                com.android.internal.R.string.config_systemUi);
 
         shellInit.addInitCallback(this::onInit, this);
     }
@@ -1035,10 +1039,14 @@
                 && taskInfo.isFocused) {
             return false;
         }
+        // TODO(b/347289970): Consider replacing with API
         if (Flags.enableDesktopWindowingModalsPolicy()
                 && isSingleTopActivityTranslucent(taskInfo)) {
             return false;
         }
+        if (isSystemUIApplication(taskInfo)) {
+            return false;
+        }
         return DesktopModeStatus.canEnterDesktopMode(mContext)
                 && !DesktopWallpaperActivity.isWallpaperTask(taskInfo)
                 && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
@@ -1109,6 +1117,14 @@
                 && mSplitScreenController.isTaskInSplitScreen(taskId);
     }
 
+    // TODO(b/347289970): Consider replacing with API
+    private boolean isSystemUIApplication(RunningTaskInfo taskInfo) {
+        if (taskInfo.baseActivity != null) {
+            return (Objects.equals(taskInfo.baseActivity.getPackageName(), mSysUIPackageName));
+        }
+        return false;
+    }
+
     private void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + "DesktopModeWindowDecorViewModel");
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 d822dfd..4d597ca 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
@@ -99,10 +99,12 @@
     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;
@@ -194,17 +196,88 @@
         // position and crop are set.
         final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled()
                 && mTaskDragResizer.isResizingOrAnimating();
-        // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is
-        // synced with the buffer transaction (that draws the View). Both will be shown on screen
-        // at the same, whereas applying them independently causes flickering. See b/270202228.
-        relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */,
-                shouldSetTaskPositionAndCrop);
+        // For headers only (i.e. in freeform): use |applyStartTransactionOnDraw| so that the
+        // transaction (that applies task crop) is synced with the buffer transaction (that draws
+        // the View). Both will be shown on screen at the same, whereas applying them independently
+        // causes flickering. See b/270202228.
+        final boolean applyTransactionOnDraw =
+                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+        relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop);
+        if (!applyTransactionOnDraw) {
+            t.apply();
+        }
     }
 
     void relayout(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
             boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
         Trace.beginSection("DesktopModeWindowDecoration#relayout");
+        if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+            // 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);
+    }
+
+    private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+            boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) {
+        Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces");
         if (isHandleMenuActive()) {
             mHandleMenu.relayout(startT);
         }
@@ -216,8 +289,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
 
@@ -228,37 +301,12 @@
         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.
-            Trace.endSection(); // DesktopModeWindowDecoration#relayout
+            Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
             return;
         }
 
         if (oldRootView != mResult.mRootView) {
-            if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
-                mWindowDecorViewHolder = new AppHandleViewHolder(
-                        mResult.mRootView,
-                        mOnCaptionTouchListener,
-                        mOnCaptionButtonClickListener
-                );
-            } else if (mRelayoutParams.mLayoutResId
-                    == R.layout.desktop_mode_app_header) {
-                loadAppInfoIfNeeded();
-                mWindowDecorViewHolder = new AppHeaderViewHolder(
-                        mResult.mRootView,
-                        mOnCaptionTouchListener,
-                        mOnCaptionButtonClickListener,
-                        mOnCaptionLongClickListener,
-                        mOnCaptionGenericMotionListener,
-                        mAppName,
-                        mAppIconBitmap,
-                        () -> {
-                            if (!isMaximizeMenuActive()) {
-                                createMaximizeMenu();
-                            }
-                            return Unit.INSTANCE;
-                        });
-            } else {
-                throw new IllegalArgumentException("Unexpected layout resource id");
-            }
+            mWindowDecorViewHolder = createViewHolder();
         }
         Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
         mWindowDecorViewHolder.bindData(mTaskInfo);
@@ -269,16 +317,18 @@
             closeMaximizeMenu();
         }
 
-        final boolean isFreeform =
-                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
-        final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
-        if (!isDragResizeable) {
+        updateDragResizeListener(oldDecorationSurface);
+        updateMaximizeMenu(startT);
+        Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
+    }
+
+    private void updateDragResizeListener(SurfaceControl oldDecorationSurface) {
+        if (!isDragResizable(mTaskInfo)) {
             if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
                 // We still want to track caption bar's exclusion region on a non-resizeable task.
                 updateExclusionRegion();
             }
             closeDragResizeListener();
-            Trace.endSection(); // DesktopModeWindowDecoration#relayout
             return;
         }
 
@@ -312,15 +362,51 @@
                 || !mTaskInfo.positionInParent.equals(mPositionInParent)) {
             updateExclusionRegion();
         }
+    }
 
-        if (isMaximizeMenuActive()) {
-            if (!mTaskInfo.isVisible()) {
-                closeMaximizeMenu();
-            } else {
-                mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
-            }
+    private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) {
+        final boolean isFreeform =
+                taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+        return isFreeform && taskInfo.isResizeable;
+    }
+
+    private void updateMaximizeMenu(SurfaceControl.Transaction startT) {
+        if (!isDragResizable(mTaskInfo) || !isMaximizeMenuActive()) {
+            return;
         }
-        Trace.endSection(); // DesktopModeWindowDecoration#relayout
+        if (!mTaskInfo.isVisible()) {
+            closeMaximizeMenu();
+        } else {
+            mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
+        }
+    }
+
+    private WindowDecorationViewHolder createViewHolder() {
+        if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) {
+            return new AppHandleViewHolder(
+                    mResult.mRootView,
+                    mOnCaptionTouchListener,
+                    mOnCaptionButtonClickListener
+            );
+        } else if (mRelayoutParams.mLayoutResId
+                == R.layout.desktop_mode_app_header) {
+            loadAppInfoIfNeeded();
+            return new AppHeaderViewHolder(
+                    mResult.mRootView,
+                    mOnCaptionTouchListener,
+                    mOnCaptionButtonClickListener,
+                    mOnCaptionLongClickListener,
+                    mOnCaptionGenericMotionListener,
+                    mAppName,
+                    mAppIconBitmap,
+                    () -> {
+                        if (!isMaximizeMenuActive()) {
+                            createMaximizeMenu();
+                        }
+                        return Unit.INSTANCE;
+                    });
+        }
+        throw new IllegalArgumentException("Unexpected layout resource id");
     }
 
     @VisibleForTesting
@@ -386,7 +472,7 @@
             // 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);
-        } else if (DesktopModeStatus.isDesktopDensityOverrideSet()) {
+        } else if (DesktopModeStatus.useDesktopOverrideDensity()) {
             // The task has had its density overridden, but keep using the system's density to
             // layout the header.
             windowDecorConfig.setTo(context.getResources().getConfiguration());
@@ -516,8 +602,8 @@
     private void createResizeVeilIfNeeded() {
         if (mResizeVeil != null) return;
         loadAppInfoIfNeeded();
-        mResizeVeil = new ResizeVeil(mContext, mDisplayController, mResizeVeilBitmap, mTaskInfo,
-                mTaskSurface, mSurfaceControlTransactionSupplier);
+        mResizeVeil = new ResizeVeil(mContext, mDisplayController, mResizeVeilBitmap,
+                mTaskSurface, mSurfaceControlTransactionSupplier, mTaskInfo);
     }
 
     /**
@@ -525,7 +611,7 @@
      */
     public void showResizeVeil(Rect taskBounds) {
         createResizeVeilIfNeeded();
-        mResizeVeil.showVeil(mTaskSurface, taskBounds);
+        mResizeVeil.showVeil(mTaskSurface, taskBounds, mTaskInfo);
     }
 
     /**
@@ -533,7 +619,7 @@
      */
     public void showResizeVeil(SurfaceControl.Transaction tx, Rect taskBounds) {
         createResizeVeilIfNeeded();
-        mResizeVeil.showVeil(tx, mTaskSurface, taskBounds, false /* fadeIn */);
+        mResizeVeil.showVeil(tx, mTaskSurface, taskBounds, mTaskInfo, false /* fadeIn */);
     }
 
     /**
@@ -818,12 +904,12 @@
         // We want handle to remain pressed if the pointer moves outside of it during a drag.
         handle.setPressed((inHandle && action == ACTION_DOWN)
                 || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL));
-        if (isHandleMenuActive() && !isMenuAboveStatusBar()) {
+        if (isHandleMenuActive() && !isHandleMenuAboveStatusBar()) {
             mHandleMenu.checkMotionEvent(ev);
         }
     }
 
-    private boolean isMenuAboveStatusBar() {
+    private boolean isHandleMenuAboveStatusBar() {
         return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform();
     }
 
@@ -838,6 +924,7 @@
         closeHandleMenu();
         mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
         disposeResizeVeil();
+        clearCurrentViewHostRunnable();
         super.close();
     }
 
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 82c399a..fe1c9c3 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
@@ -22,12 +22,16 @@
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED;
 
+import android.content.Context;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.SurfaceControl;
 
+import com.android.window.flags.Flags;
+import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.shared.DesktopModeStatus;
 
 /**
  * Utility class that contains logic common to classes implementing {@link DragPositioningCallback}
@@ -35,11 +39,11 @@
  * and applying that change to the task bounds when applicable.
  */
 public class DragPositioningCallbackUtility {
-
     /**
      * Determine the delta between input's current point and the input start point.
-     * @param inputX current input x coordinate
-     * @param inputY current input y coordinate
+     *
+     * @param inputX               current input x coordinate
+     * @param inputY               current input y coordinate
      * @param repositionStartPoint initial input coordinate
      * @return delta between these two points
      */
@@ -52,13 +56,14 @@
     /**
      * Based on type of resize and delta provided, calculate the new bounds to display for this
      * task.
-     * @param ctrlType type of drag being performed
-     * @param repositionTaskBounds the bounds the task is being repositioned to
+     *
+     * @param ctrlType              type of drag being performed
+     * @param repositionTaskBounds  the bounds the task is being repositioned to
      * @param taskBoundsAtDragStart the bounds of the task on the first drag input event
-     * @param stableBounds bounds that represent the resize limit of this task
-     * @param delta difference between start input and current input in x/y coordinates
-     * @param displayController task's display controller
-     * @param windowDecoration window decoration of the task being dragged
+     * @param stableBounds          bounds that represent the resize limit of this task
+     * @param delta                 difference between start input and current input in x/y
+     *                              coordinates
+     * @param windowDecoration      window decoration of the task being dragged
      * @return whether this method changed repositionTaskBounds
      */
     static boolean changeBounds(int ctrlType, Rect repositionTaskBounds, Rect taskBoundsAtDragStart,
@@ -142,8 +147,9 @@
     /**
      * If task bounds are outside of provided drag area, snap the bounds to be just inside the
      * drag area.
+     *
      * @param repositionTaskBounds bounds determined by task positioner
-     * @param validDragArea the area that task must be positioned inside
+     * @param validDragArea        the area that task must be positioned inside
      * @return whether bounds were modified
      */
     public static boolean snapTaskBoundsIfNecessary(Rect repositionTaskBounds, Rect validDragArea) {
@@ -170,18 +176,38 @@
 
     private static float getMinWidth(DisplayController displayController,
             WindowDecoration windowDecoration) {
-        return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinSize(displayController,
+        return windowDecoration.mTaskInfo.minWidth < 0 ? getDefaultMinWidth(displayController,
                 windowDecoration)
                 : windowDecoration.mTaskInfo.minWidth;
     }
 
     private static float getMinHeight(DisplayController displayController,
             WindowDecoration windowDecoration) {
-        return windowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinSize(displayController,
+        return windowDecoration.mTaskInfo.minHeight < 0 ? getDefaultMinHeight(displayController,
                 windowDecoration)
                 : windowDecoration.mTaskInfo.minHeight;
     }
 
+    private static float getDefaultMinWidth(DisplayController displayController,
+            WindowDecoration windowDecoration) {
+        if (isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)) {
+            return WindowDecoration.loadDimensionPixelSize(
+                    windowDecoration.mDecorWindowContext.getResources(),
+                    R.dimen.desktop_mode_minimum_window_width);
+        }
+        return getDefaultMinSize(displayController, windowDecoration);
+    }
+
+    private static float getDefaultMinHeight(DisplayController displayController,
+            WindowDecoration windowDecoration) {
+        if (isSizeConstraintForDesktopModeEnabled(windowDecoration.mDecorWindowContext)) {
+            return WindowDecoration.loadDimensionPixelSize(
+                    windowDecoration.mDecorWindowContext.getResources(),
+                    R.dimen.desktop_mode_minimum_window_height);
+        }
+        return getDefaultMinSize(displayController, windowDecoration);
+    }
+
     private static float getDefaultMinSize(DisplayController displayController,
             WindowDecoration windowDecoration) {
         float density =  displayController.getDisplayLayout(windowDecoration.mTaskInfo.displayId)
@@ -189,9 +215,15 @@
         return windowDecoration.mTaskInfo.defaultMinSize * density;
     }
 
+    private static boolean isSizeConstraintForDesktopModeEnabled(Context context) {
+        return DesktopModeStatus.canEnterDesktopMode(context)
+                && Flags.enableDesktopWindowingSizeConstraints();
+    }
+
     interface DragStartListener {
         /**
          * Inform the implementing class that a drag resize has started
+         *
          * @param taskId id of this positioner's {@link WindowDecoration}
          */
         void onDragStart(int taskId);
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 badce6e..d902444 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
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.windowdecor;
 
-import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
@@ -29,6 +28,8 @@
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_LEFT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT;
 import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEdgeResizePermitted;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.isEventFromTouchscreen;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -285,6 +286,9 @@
         private boolean mShouldHandleEvents;
         private int mLastCursorType = PointerIcon.TYPE_DEFAULT;
         private Rect mDragStartTaskBounds;
+        // The id of the particular pointer in a MotionEvent that we are listening to for drag
+        // resize events. For example, if multiple fingers are touching the screen, then each one
+        // has a separate pointer id, but we only accept drag input from one.
         private int mDragPointerId = -1;
 
         private TaskResizeInputEventReceiver(@NonNull Context context,
@@ -389,18 +393,20 @@
             boolean result = false;
             // Check if this is a touch event vs mouse event.
             // Touch events are tracked in four corners. Other events are tracked in resize edges.
-            boolean isTouch = isTouchEvent(e);
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
-                    mShouldHandleEvents = mDragResizeWindowGeometry.shouldHandleEvent(e, isTouch,
+                    mShouldHandleEvents = mDragResizeWindowGeometry.shouldHandleEvent(e,
                             new Point() /* offset */);
                     if (mShouldHandleEvents) {
+                        // Save the id of the pointer for this drag interaction; we will use the
+                        // same pointer for all subsequent MotionEvents in this interaction.
                         mDragPointerId = e.getPointerId(0);
                         float x = e.getX(0);
                         float y = e.getY(0);
                         float rawX = e.getRawX(0);
                         float rawY = e.getRawY(0);
-                        int ctrlType = mDragResizeWindowGeometry.calculateCtrlType(isTouch, x, y);
+                        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);
                         mDragStartTaskBounds = mCallback.onDragPositioningStart(ctrlType,
@@ -420,9 +426,16 @@
                         break;
                     }
                     mInputManager.pilferPointers(mInputChannel.getToken());
-                    int dragPointerIndex = e.findPointerIndex(mDragPointerId);
-                    float rawX = e.getRawX(dragPointerIndex);
-                    float rawY = e.getRawY(dragPointerIndex);
+                    final int dragPointerIndex = e.findPointerIndex(mDragPointerId);
+                    if (dragPointerIndex < 0) {
+                        ProtoLog.d(WM_SHELL_DESKTOP_MODE,
+                                "%s: Handling action move, but ignore event due to invalid "
+                                        + "pointer index",
+                                TAG);
+                        break;
+                    }
+                    final float rawX = e.getRawX(dragPointerIndex);
+                    final float rawY = e.getRawY(dragPointerIndex);
                     final Rect taskBounds = mCallback.onDragPositioningMove(rawX, rawY);
                     updateInputSinkRegionForDrag(taskBounds);
                     result = true;
@@ -431,7 +444,14 @@
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL: {
                     if (mShouldHandleEvents) {
-                        int dragPointerIndex = e.findPointerIndex(mDragPointerId);
+                        final int dragPointerIndex = e.findPointerIndex(mDragPointerId);
+                        if (dragPointerIndex < 0) {
+                            ProtoLog.d(WM_SHELL_DESKTOP_MODE,
+                                    "%s: Handling action %d, but ignore event due to invalid "
+                                            + "pointer index",
+                                    TAG, e.getActionMasked());
+                            break;
+                        }
                         final Rect taskBounds = mCallback.onDragPositioningEnd(
                                 e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
                         // If taskBounds has changed, setGeometry will be called and update the
@@ -474,8 +494,11 @@
 
         private void updateCursorType(int displayId, int deviceId, int pointerId, float x,
                 float y) {
+            // 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(/* isTouch= */ false, x, y);
+                    mDragResizeWindowGeometry.calculateCtrlType(/* isTouchscreen= */
+                            false, /* isEdgeResizePermitted= */ true, x, y);
 
             int cursorType = PointerIcon.TYPE_DEFAULT;
             switch (ctrlType) {
@@ -517,9 +540,5 @@
         private boolean shouldHandleEvent(MotionEvent e, Point offset) {
             return mDragResizeWindowGeometry.shouldHandleEvent(e, offset);
         }
-
-        private boolean isTouchEvent(MotionEvent e) {
-            return (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
-        }
     }
 }
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 4f513f0..b5d1d4a 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
@@ -142,40 +142,42 @@
      * Returns if this MotionEvent should be handled, based on its source and position.
      */
     boolean shouldHandleEvent(@NonNull MotionEvent e, @NonNull Point offset) {
-        return shouldHandleEvent(e, isTouchEvent(e), offset);
-    }
-
-    /**
-     * Returns if this MotionEvent should be handled, based on its source and position.
-     */
-    boolean shouldHandleEvent(@NonNull MotionEvent e, boolean isTouch, @NonNull Point offset) {
         final float x = e.getX(0) + offset.x;
         final float y = e.getY(0) + offset.y;
 
         if (enableWindowingEdgeDragResize()) {
             // First check if touch falls within a corner.
             // Large corner bounds are used for course input like touch, otherwise fine bounds.
-            boolean result = isTouch
+            boolean result = isEventFromTouchscreen(e)
                     ? isInCornerBounds(mLargeTaskCorners, x, y)
                     : isInCornerBounds(mFineTaskCorners, x, y);
-            // Check if touch falls within the edge resize handle, since edge resizing can apply
-            // for any input source.
-            if (!result) {
+            // Check if touch falls within the edge resize handle. Limit edge resizing to stylus and
+            // mouse input.
+            if (!result && isEdgeResizePermitted(e)) {
                 result = isInEdgeResizeBounds(x, y);
             }
             return result;
         } else {
             // Legacy uses only fine corners for touch, and edges only for non-touch input.
-            return isTouch
+            return isEventFromTouchscreen(e)
                     ? isInCornerBounds(mFineTaskCorners, x, y)
                     : isInEdgeResizeBounds(x, y);
         }
     }
 
-    private boolean isTouchEvent(@NonNull MotionEvent e) {
+    static boolean isEventFromTouchscreen(@NonNull MotionEvent e) {
         return (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
     }
 
+    static boolean isEdgeResizePermitted(@NonNull MotionEvent e) {
+        if (enableWindowingEdgeDragResize()) {
+            return e.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
+                    || e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
+        } else {
+            return e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
+        }
+    }
+
     private boolean isInCornerBounds(TaskCorners corners, float xf, float yf) {
         return corners.calculateCornersCtrlType(xf, yf) != 0;
     }
@@ -187,24 +189,29 @@
     /**
      * Returns the control type for the drag-resize, based on the touch regions and this
      * MotionEvent's coordinates.
+     * @param isTouchscreen Controls the size of the corner resize regions; touchscreen events
+     *                      (finger & stylus) are eligible for a larger area than cursor events
+     * @param isEdgeResizePermitted Indicates if the event is eligible for falling into an edge
+     *                              resize region.
      */
     @DragPositioningCallback.CtrlType
-    int calculateCtrlType(boolean isTouch, float x, float y) {
+    int calculateCtrlType(boolean isTouchscreen, boolean isEdgeResizePermitted, float x, float y) {
         if (enableWindowingEdgeDragResize()) {
             // First check if touch falls within a corner.
             // Large corner bounds are used for course input like touch, otherwise fine bounds.
-            int ctrlType = isTouch
+            int ctrlType = isTouchscreen
                     ? mLargeTaskCorners.calculateCornersCtrlType(x, y)
                     : mFineTaskCorners.calculateCornersCtrlType(x, y);
+
             // Check if touch falls within the edge resize handle, since edge resizing can apply
             // for any input source.
-            if (ctrlType == CTRL_TYPE_UNDEFINED) {
+            if (ctrlType == CTRL_TYPE_UNDEFINED && isEdgeResizePermitted) {
                 ctrlType = calculateEdgeResizeCtrlType(x, y);
             }
             return ctrlType;
         } else {
             // Legacy uses only fine corners for touch, and edges only for non-touch input.
-            return isTouch
+            return isTouchscreen
                     ? mFineTaskCorners.calculateCornersCtrlType(x, y)
                     : calculateEdgeResizeCtrlType(x, y);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index bfc4e0d..df0836c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -24,6 +24,7 @@
 import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -35,6 +36,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Color;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.view.MotionEvent;
@@ -70,8 +72,14 @@
     private final DesktopModeWindowDecoration mParentDecor;
     @VisibleForTesting
     AdditionalViewContainer mHandleMenuViewContainer;
+    // Position of the handle menu used for laying out the handle view.
     @VisibleForTesting
     final PointF mHandleMenuPosition = new PointF();
+    // With the introduction of {@link AdditionalSystemViewContainer}, {@link mHandleMenuPosition}
+    // may be in a different coordinate space than the input coordinates. Therefore, we still care
+    // about the menu's coordinates relative to the display as a whole, so we need to maintain
+    // those as well.
+    final Point mGlobalMenuPosition = new Point();
     private final boolean mShouldShowWindowingPill;
     private final Bitmap mAppIconBitmap;
     private final CharSequence mAppName;
@@ -244,39 +252,23 @@
     private void updateHandleMenuPillPositions() {
         int menuX;
         final int menuY;
+        final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
+        updateGlobalMenuPosition(taskBounds);
         if (mLayoutResId == R.layout.desktop_mode_app_header) {
             // Align the handle menu to the left side of the caption.
             menuX = mMarginMenuStart;
             menuY = mMarginMenuTop;
         } else {
-            final int handleWidth = loadDimensionPixelSize(mContext.getResources(),
-                    R.dimen.desktop_mode_fullscreen_decor_caption_width);
-            final int handleOffset = (mMenuWidth / 2) - (handleWidth / 2);
-            final int captionX = mParentDecor.getCaptionX();
-            // TODO(b/343561161): This needs to be calculated differently if the task is in
-            //  top/bottom split.
             if (Flags.enableAdditionalWindowsAboveStatusBar()) {
-                final Rect leftOrTopStageBounds = new Rect();
-                if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
-                        == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
-                    mSplitScreenController.getStageBounds(leftOrTopStageBounds, new Rect());
-                }
                 // In a focused decor, we use global coordinates for handle menu. Therefore we
                 // need to account for other factors like split stage and menu/handle width to
                 // center the menu.
                 final DisplayLayout layout = mDisplayController
                         .getDisplayLayout(mTaskInfo.displayId);
-                menuX = captionX + handleOffset - (layout.width() / 2);
-                if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
-                        == SPLIT_POSITION_BOTTOM_OR_RIGHT && layout.isLandscape()) {
-                    // If this task in the right stage, we need to offset by left stage's width
-                    menuX += leftOrTopStageBounds.width();
-                }
-                menuY = mMarginMenuStart - ((layout.height() - mMenuHeight) / 2);
+                menuX = mGlobalMenuPosition.x + ((mMenuWidth - layout.width()) / 2);
+                menuY = mGlobalMenuPosition.y + ((mMenuHeight - layout.height()) / 2);
             } else {
-                final int captionWidth = mTaskInfo.getConfiguration()
-                        .windowConfiguration.getBounds().width();
-                menuX = (captionWidth / 2) - (mMenuWidth / 2);
+                menuX = (taskBounds.width() / 2) - (mMenuWidth / 2);
                 menuY = mMarginMenuTop;
             }
         }
@@ -284,6 +276,36 @@
         mHandleMenuPosition.set(menuX, menuY);
     }
 
+    private void updateGlobalMenuPosition(Rect taskBounds) {
+        if (mTaskInfo.isFreeform()) {
+            mGlobalMenuPosition.set(taskBounds.left + mMarginMenuStart,
+                    taskBounds.top + mMarginMenuTop);
+        } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+            mGlobalMenuPosition.set(
+                    (taskBounds.width() / 2) - (mMenuWidth / 2) + mMarginMenuStart,
+                    mMarginMenuTop
+            );
+        } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+            final int splitPosition = mSplitScreenController.getSplitPosition(mTaskInfo.taskId);
+            final Rect leftOrTopStageBounds = new Rect();
+            final Rect rightOrBottomStageBounds = new Rect();
+            mSplitScreenController.getStageBounds(leftOrTopStageBounds,
+                    rightOrBottomStageBounds);
+            // TODO(b/343561161): This needs to be calculated differently if the task is in
+            //  top/bottom split.
+            if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+                mGlobalMenuPosition.set(leftOrTopStageBounds.width()
+                                + (rightOrBottomStageBounds.width() / 2)
+                                - (mMenuWidth / 2) + mMarginMenuStart,
+                        mMarginMenuTop);
+            } else if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
+                mGlobalMenuPosition.set((leftOrTopStageBounds.width() / 2)
+                                - (mMenuWidth / 2) + mMarginMenuStart,
+                        mMarginMenuTop);
+            }
+        }
+    }
+
     /**
      * Update pill layout, in case task changes have caused positioning to change.
      */
@@ -302,6 +324,8 @@
      * @param ev the MotionEvent to compare against.
      */
     void checkMotionEvent(MotionEvent ev) {
+        // If the menu view is above status bar, we can let the views handle input directly.
+        if (isViewAboveStatusBar()) return;
         final View handleMenu = mHandleMenuViewContainer.getView();
         final HandleMenuImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button);
         final PointF inputPoint = translateInputToLocalSpace(ev);
@@ -314,6 +338,11 @@
         }
     }
 
+    private boolean isViewAboveStatusBar() {
+        return Flags.enableAdditionalWindowsAboveStatusBar()
+                && !mTaskInfo.isFreeform();
+    }
+
     // Translate the input point from display coordinates to the same space as the handle menu.
     private PointF translateInputToLocalSpace(MotionEvent ev) {
         return new PointF(ev.getX() - mHandleMenuPosition.x,
@@ -329,10 +358,33 @@
      */
     boolean isValidMenuInput(PointF inputPoint) {
         if (!viewsLaidOut()) return true;
-        return pointInView(
-                mHandleMenuViewContainer.getView(),
-                inputPoint.x - mHandleMenuPosition.x,
-                inputPoint.y - mHandleMenuPosition.y);
+        if (!isViewAboveStatusBar()) {
+            return pointInView(
+                    mHandleMenuViewContainer.getView(),
+                    inputPoint.x - mHandleMenuPosition.x,
+                    inputPoint.y - mHandleMenuPosition.y);
+        } else {
+            // Handle menu exists in a different coordinate space when added to WindowManager.
+            // Therefore we must compare the provided input coordinates to global menu coordinates.
+            // This includes factoring for split stage as input coordinates are relative to split
+            // stage position, not relative to the display as a whole.
+            PointF inputRelativeToMenu = new PointF(
+                    inputPoint.x - mGlobalMenuPosition.x,
+                    inputPoint.y - mGlobalMenuPosition.y
+            );
+            if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
+                    == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
+                // TODO(b/343561161): This also needs to be calculated differently if
+                //  the task is in top/bottom split.
+                Rect leftStageBounds = new Rect();
+                mSplitScreenController.getStageBounds(leftStageBounds, new Rect());
+                inputRelativeToMenu.x += leftStageBounds.width();
+            }
+            return pointInView(
+                    mHandleMenuViewContainer.getView(),
+                    inputRelativeToMenu.x,
+                    inputRelativeToMenu.y);
+        }
     }
 
     private boolean pointInView(View v, float x, float y) {
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 c903d3b..0470367 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
@@ -19,17 +19,28 @@
 import android.animation.AnimatorSet
 import android.animation.ObjectAnimator
 import android.animation.ValueAnimator
+import android.annotation.ColorInt
 import android.annotation.IdRes
 import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
+import android.content.res.ColorStateList
 import android.content.res.Resources
+import android.graphics.Paint
 import android.graphics.PixelFormat
 import android.graphics.PointF
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.StateListDrawable
+import android.graphics.drawable.shapes.RoundRectShape
+import android.util.StateSet
 import android.view.LayoutInflater
 import android.view.MotionEvent
 import android.view.SurfaceControl
 import android.view.SurfaceControl.Transaction
 import android.view.SurfaceControlViewHost
+import android.view.View
 import android.view.View.OnClickListener
 import android.view.View.OnGenericMotionListener
 import android.view.View.OnTouchListener
@@ -39,18 +50,21 @@
 import android.view.WindowManager
 import android.view.WindowlessWindowManager
 import android.widget.Button
-import android.widget.FrameLayout
-import android.widget.LinearLayout
 import android.widget.TextView
 import android.window.TaskConstants
-import androidx.core.content.withStyledAttributes
-import com.android.internal.R.attr.colorAccentPrimary
+import androidx.compose.material3.ColorScheme
+import androidx.compose.ui.graphics.toArgb
+import androidx.core.animation.addListener
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.animation.Interpolators.EMPHASIZED_DECELERATE
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
+import com.android.wm.shell.windowdecor.common.OPACITY_12
+import com.android.wm.shell.windowdecor.common.OPACITY_40
+import com.android.wm.shell.windowdecor.common.withAlpha
 import java.util.function.Supplier
 
 
@@ -71,9 +85,9 @@
         private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
 ) {
     private var maximizeMenu: AdditionalViewHostViewContainer? = null
+    private var maximizeMenuView: MaximizeMenuView? = null
     private lateinit var viewHost: SurfaceControlViewHost
     private lateinit var leash: SurfaceControl
-    private val openMenuAnimatorSet = AnimatorSet()
     private val cornerRadius = loadDimensionPixelSize(
             R.dimen.desktop_mode_maximize_menu_corner_radius
     ).toFloat()
@@ -81,12 +95,6 @@
     private val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
     private val menuPadding = loadDimensionPixelSize(R.dimen.desktop_mode_menu_padding)
 
-    private lateinit var snapRightButton: Button
-    private lateinit var snapLeftButton: Button
-    private lateinit var maximizeButton: Button
-    private lateinit var maximizeButtonLayout: FrameLayout
-    private lateinit var snapButtonsLayout: LinearLayout
-
     /** Position the menu relative to the caption's position. */
     fun positionMenu(position: PointF, t: Transaction) {
         menuPosition.set(position)
@@ -97,24 +105,20 @@
     fun show() {
         if (maximizeMenu != null) return
         createMaximizeMenu()
-        setupMaximizeMenu()
-        animateOpenMenu()
+        maximizeMenuView?.animateOpenMenu()
     }
 
     /** Closes the maximize window and releases its view. */
     fun close() {
-        openMenuAnimatorSet.cancel()
+        maximizeMenuView?.cancelAnimation()
         maximizeMenu?.releaseView()
         maximizeMenu = null
+        maximizeMenuView = null
     }
 
     /** Create a maximize menu that is attached to the display area. */
     private fun createMaximizeMenu() {
         val t = transactionSupplier.get()
-        val v = LayoutInflater.from(decorWindowContext).inflate(
-                R.layout.desktop_mode_window_decor_maximize_menu,
-                null // Root
-        )
         val builder = SurfaceControl.Builder()
         rootTdaOrganizer.attachToDisplayArea(taskInfo.displayId, builder)
         leash = builder
@@ -138,7 +142,17 @@
         viewHost = SurfaceControlViewHost(decorWindowContext,
                 displayController.getDisplay(taskInfo.displayId), windowManager,
                 "MaximizeMenu")
-        viewHost.setView(v, lp)
+        maximizeMenuView = MaximizeMenuView(
+            context = decorWindowContext,
+            menuHeight = menuHeight,
+            menuPadding = menuPadding,
+            onClickListener = onClickListener,
+            onTouchListener = onTouchListener,
+            onGenericMotionListener = onGenericMotionListener,
+        ).also { menuView ->
+            menuView.bind(taskInfo)
+            viewHost.setView(menuView.rootView, lp)
+        }
 
         // Bring menu to front when open
         t.setLayer(leash, TaskConstants.TASK_CHILD_LAYER_FLOATING_MENU)
@@ -154,76 +168,6 @@
         }
     }
 
-    private fun animateOpenMenu() {
-        val maximizeMenuView = maximizeMenu?.view ?: return
-        val maximizeWindowText = maximizeMenuView.requireViewById<TextView>(
-                R.id.maximize_menu_maximize_window_text)
-        val snapWindowText = maximizeMenuView.requireViewById<TextView>(
-                R.id.maximize_menu_snap_window_text)
-
-        openMenuAnimatorSet.playTogether(
-                ObjectAnimator.ofFloat(maximizeMenuView, SCALE_Y, STARTING_MENU_HEIGHT_SCALE, 1f)
-                        .apply {
-                            duration = MENU_HEIGHT_ANIMATION_DURATION_MS
-                            interpolator = EMPHASIZED_DECELERATE
-                        },
-                ValueAnimator.ofFloat(STARTING_MENU_HEIGHT_SCALE, 1f)
-                        .apply {
-                            duration = MENU_HEIGHT_ANIMATION_DURATION_MS
-                            interpolator = EMPHASIZED_DECELERATE
-                            addUpdateListener {
-                                // Animate padding so that controls stay pinned to the bottom of
-                                // the menu.
-                                val value = animatedValue as Float
-                                val topPadding = menuPadding -
-                                        ((1 - value) * menuHeight).toInt()
-                                maximizeMenuView.setPadding(menuPadding, topPadding,
-                                        menuPadding, menuPadding)
-                            }
-                        },
-                ValueAnimator.ofFloat(1 / STARTING_MENU_HEIGHT_SCALE, 1f).apply {
-                            duration = MENU_HEIGHT_ANIMATION_DURATION_MS
-                            interpolator = EMPHASIZED_DECELERATE
-                            addUpdateListener {
-                                // Scale up the children of the maximize menu so that the menu
-                                // scale is cancelled out and only the background is scaled.
-                                val value = animatedValue as Float
-                                maximizeButtonLayout.scaleY = value
-                                snapButtonsLayout.scaleY = value
-                                maximizeWindowText.scaleY = value
-                                snapWindowText.scaleY = value
-                            }
-                        },
-                ObjectAnimator.ofFloat(maximizeMenuView, TRANSLATION_Y,
-                        (STARTING_MENU_HEIGHT_SCALE - 1) * menuHeight, 0f).apply {
-                    duration = MENU_HEIGHT_ANIMATION_DURATION_MS
-                    interpolator = EMPHASIZED_DECELERATE
-                },
-                ObjectAnimator.ofInt(maximizeMenuView.background, "alpha",
-                        MAX_DRAWABLE_ALPHA_VALUE).apply {
-                    duration = ALPHA_ANIMATION_DURATION_MS
-                },
-                ValueAnimator.ofFloat(0f, 1f)
-                        .apply {
-                            duration = ALPHA_ANIMATION_DURATION_MS
-                            startDelay = CONTROLS_ALPHA_ANIMATION_DELAY_MS
-                            addUpdateListener {
-                                val value = animatedValue as Float
-                                maximizeButtonLayout.alpha = value
-                                snapButtonsLayout.alpha = value
-                                maximizeWindowText.alpha = value
-                                snapWindowText.alpha = value
-                            }
-                        },
-                ObjectAnimator.ofFloat(maximizeMenuView, TRANSLATION_Z, MENU_Z_TRANSLATION)
-                        .apply {
-                            duration = ELEVATION_ANIMATION_DURATION_MS
-                            startDelay = CONTROLS_ALPHA_ANIMATION_DELAY_MS
-                        }
-        )
-        openMenuAnimatorSet.start()
-    }
-
     private fun loadDimensionPixelSize(resourceId: Int): Int {
         return if (resourceId == Resources.ID_NULL) {
             0
@@ -232,31 +176,6 @@
         }
     }
 
-    private fun setupMaximizeMenu() {
-        val maximizeMenuView = maximizeMenu?.view ?: return
-
-        maximizeMenuView.setOnGenericMotionListener(onGenericMotionListener)
-        maximizeMenuView.setOnTouchListener(onTouchListener)
-
-        maximizeButtonLayout = maximizeMenuView.requireViewById(
-                R.id.maximize_menu_maximize_button_layout)
-
-        maximizeButton = maximizeMenuView.requireViewById(R.id.maximize_menu_maximize_button)
-        maximizeButton.setOnClickListener(onClickListener)
-        maximizeButton.setOnGenericMotionListener(onGenericMotionListener)
-
-        snapRightButton = maximizeMenuView.requireViewById(R.id.maximize_menu_snap_right_button)
-        snapRightButton.setOnClickListener(onClickListener)
-        snapRightButton.setOnGenericMotionListener(onGenericMotionListener)
-
-        snapLeftButton = maximizeMenuView.requireViewById(R.id.maximize_menu_snap_left_button)
-        snapLeftButton.setOnClickListener(onClickListener)
-        snapLeftButton.setOnGenericMotionListener(onGenericMotionListener)
-
-        snapButtonsLayout = maximizeMenuView.requireViewById(R.id.maximize_menu_snap_menu_layout)
-        snapButtonsLayout.setOnGenericMotionListener(onGenericMotionListener)
-    }
-
     /**
      * A valid menu input is one of the following:
      * An input that happens in the menu views.
@@ -278,65 +197,435 @@
         return maximizeMenu?.view?.isLaidOut ?: false
     }
 
+    /**
+     * Called when a [MotionEvent.ACTION_HOVER_ENTER] is triggered on any of the menu's views.
+     *
+     * TODO(b/346440693): this is only needed for the left/right snap options that don't support
+     *  selector states to manage its hover state. Look into whether that can be added to avoid
+     *  manually tracking hover enter/exit motion events. Also because those button colors/states
+     *  aren't updating correctly for pressed, focused and selected states.
+     *  See also [onMaximizeMenuHoverMove] and [onMaximizeMenuHoverExit].
+     */
     fun onMaximizeMenuHoverEnter(viewId: Int, ev: MotionEvent) {
         setSnapButtonsColorOnHover(viewId, ev)
     }
 
+    /** Called when a [MotionEvent.ACTION_HOVER_MOVE] is triggered on any of the menu's views. */
     fun onMaximizeMenuHoverMove(viewId: Int, ev: MotionEvent) {
         setSnapButtonsColorOnHover(viewId, ev)
     }
 
+    /** Called when a [MotionEvent.ACTION_HOVER_EXIT] is triggered on any of the menu's views. */
     fun onMaximizeMenuHoverExit(id: Int, ev: MotionEvent) {
-        val inSnapMenuBounds = ev.x >= 0 && ev.x <= snapButtonsLayout.width &&
-                ev.y >= 0 && ev.y <= snapButtonsLayout.height
-        val colorList = decorWindowContext.getColorStateList(
-                R.color.desktop_mode_maximize_menu_button_color_selector)
+        val snapOptionsWidth = maximizeMenuView?.snapOptionsWidth ?: return
+        val snapOptionsHeight = maximizeMenuView?.snapOptionsHeight ?: return
+        val inSnapMenuBounds = ev.x >= 0 && ev.x <= snapOptionsWidth &&
+                ev.y >= 0 && ev.y <= snapOptionsHeight
 
-        if (id == R.id.maximize_menu_maximize_button) {
-            maximizeButton.background?.setTintList(colorList)
-            maximizeButtonLayout.setBackgroundResource(
-                    R.drawable.desktop_mode_maximize_menu_layout_background)
-        } else if (id == R.id.maximize_menu_snap_menu_layout && !inSnapMenuBounds) {
+        if (id == R.id.maximize_menu_snap_menu_layout && !inSnapMenuBounds) {
             // After exiting the snap menu layout area, checks to see that user is not still
             // hovering within the snap menu layout bounds which would indicate that the user is
             // hovering over a snap button within the snap menu layout rather than having exited.
-            snapLeftButton.background?.setTintList(colorList)
-            snapLeftButton.background?.alpha = 255
-            snapRightButton.background?.setTintList(colorList)
-            snapRightButton.background?.alpha = 255
-            snapButtonsLayout.setBackgroundResource(
-                    R.drawable.desktop_mode_maximize_menu_layout_background)
+            maximizeMenuView?.updateSplitSnapSelection(MaximizeMenuView.SnapToHalfSelection.NONE)
         }
     }
 
     private fun setSnapButtonsColorOnHover(viewId: Int, ev: MotionEvent) {
-        decorWindowContext.withStyledAttributes(null, intArrayOf(colorAccentPrimary), 0, 0) {
-            val materialColor = getColor(0, 0)
-            val snapMenuCenter = snapButtonsLayout.width / 2
-            if (viewId == R.id.maximize_menu_maximize_button) {
-                // Highlight snap maximize window button
-                maximizeButton.background?.setTint(materialColor)
-                maximizeButtonLayout.setBackgroundResource(
-                        R.drawable.desktop_mode_maximize_menu_layout_background_on_hover)
-            } else if (viewId == R.id.maximize_menu_snap_left_button ||
-                    (viewId == R.id.maximize_menu_snap_menu_layout && ev.x <= snapMenuCenter)) {
-                // Highlight snap left button
-                snapRightButton.background?.setTint(materialColor)
-                snapLeftButton.background?.setTint(materialColor)
-                snapButtonsLayout.setBackgroundResource(
-                        R.drawable.desktop_mode_maximize_menu_layout_background_on_hover)
-                snapRightButton.background?.alpha = 102
-                snapLeftButton.background?.alpha = 255
-            } else if (viewId == R.id.maximize_menu_snap_right_button ||
-                    (viewId == R.id.maximize_menu_snap_menu_layout && ev.x > snapMenuCenter)) {
-                // Highlight snap right button
-                snapRightButton.background?.setTint(materialColor)
-                snapLeftButton.background?.setTint(materialColor)
-                snapButtonsLayout.setBackgroundResource(
-                        R.drawable.desktop_mode_maximize_menu_layout_background_on_hover)
-                snapRightButton.background?.alpha = 255
-                snapLeftButton.background?.alpha = 102
+        val snapOptionsWidth = maximizeMenuView?.snapOptionsWidth ?: return
+        val snapMenuCenter = snapOptionsWidth / 2
+        when {
+            viewId == R.id.maximize_menu_snap_left_button ||
+                    (viewId == R.id.maximize_menu_snap_menu_layout && ev.x <= snapMenuCenter) -> {
+                        maximizeMenuView
+                            ?.updateSplitSnapSelection(MaximizeMenuView.SnapToHalfSelection.LEFT)
             }
+            viewId == R.id.maximize_menu_snap_right_button ||
+                    (viewId == R.id.maximize_menu_snap_menu_layout && ev.x > snapMenuCenter) -> {
+                        maximizeMenuView
+                            ?.updateSplitSnapSelection(MaximizeMenuView.SnapToHalfSelection.RIGHT)
+                    }
+        }
+    }
+
+    /**
+     * The view within the Maximize Menu, presents maximize, restore and snap-to-side options for
+     * resizing a Task.
+     */
+    class MaximizeMenuView(
+        context: Context,
+        private val menuHeight: Int,
+        private val menuPadding: Int,
+        onClickListener: OnClickListener,
+        onTouchListener: OnTouchListener,
+        onGenericMotionListener: OnGenericMotionListener,
+    ) {
+        val rootView: View = LayoutInflater.from(context)
+            .inflate(R.layout.desktop_mode_window_decor_maximize_menu, null /* root */)
+        private val maximizeText =
+            requireViewById(R.id.maximize_menu_maximize_window_text) as TextView
+        private val maximizeButton =
+            requireViewById(R.id.maximize_menu_maximize_button) as Button
+        private val snapWindowText =
+            requireViewById(R.id.maximize_menu_snap_window_text) as TextView
+        private val snapRightButton =
+            requireViewById(R.id.maximize_menu_snap_right_button) as Button
+        private val snapLeftButton =
+            requireViewById(R.id.maximize_menu_snap_left_button) as Button
+        private val snapButtonsLayout =
+            requireViewById(R.id.maximize_menu_snap_menu_layout)
+
+        private val decorThemeUtil = DecorThemeUtil(context)
+
+        private val outlineRadius = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_outline_radius)
+        private val outlineStroke = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_outline_stroke)
+        private val fillPadding = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_padding)
+        private val fillRadius = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_radius)
+
+        private val openMenuAnimatorSet = AnimatorSet()
+        private lateinit var taskInfo: RunningTaskInfo
+        private lateinit var style: MenuStyle
+
+        /** The width of the snap menu option view, including both left and right snaps. */
+        val snapOptionsWidth: Int
+            get() = snapButtonsLayout.width
+        /** The height of the snap menu option view, including both left and right snaps .*/
+        val snapOptionsHeight: Int
+            get() = snapButtonsLayout.height
+
+        init {
+            // TODO(b/346441962): encapsulate menu hover enter/exit logic inside this class and
+            //  expose only what  is actually relevant to outside classes so that specific checks
+            //  against resource IDs aren't needed outside this class.
+            rootView.setOnGenericMotionListener(onGenericMotionListener)
+            rootView.setOnTouchListener(onTouchListener)
+            maximizeButton.setOnClickListener(onClickListener)
+            maximizeButton.setOnGenericMotionListener(onGenericMotionListener)
+            snapRightButton.setOnClickListener(onClickListener)
+            snapRightButton.setOnGenericMotionListener(onGenericMotionListener)
+            snapLeftButton.setOnClickListener(onClickListener)
+            snapLeftButton.setOnGenericMotionListener(onGenericMotionListener)
+            snapButtonsLayout.setOnGenericMotionListener(onGenericMotionListener)
+
+            // To prevent aliasing.
+            maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+            maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+        }
+
+        /** Bind the menu views to the new [RunningTaskInfo] data. */
+        fun bind(taskInfo: RunningTaskInfo) {
+            this.taskInfo = taskInfo
+            this.style = calculateMenuStyle(taskInfo)
+
+            rootView.background.setTint(style.backgroundColor)
+
+            // Maximize option.
+            maximizeButton.background = style.maximizeOption.drawable
+            maximizeText.setTextColor(style.textColor)
+
+            // Snap options.
+            snapWindowText.setTextColor(style.textColor)
+            updateSplitSnapSelection(SnapToHalfSelection.NONE)
+        }
+
+        /** Animate the opening of the menu */
+        fun animateOpenMenu() {
+            maximizeButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            maximizeText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+            openMenuAnimatorSet.playTogether(
+                ObjectAnimator.ofFloat(rootView, SCALE_Y, STARTING_MENU_HEIGHT_SCALE, 1f)
+                    .apply {
+                        duration = MENU_HEIGHT_ANIMATION_DURATION_MS
+                        interpolator = EMPHASIZED_DECELERATE
+                    },
+                ValueAnimator.ofFloat(STARTING_MENU_HEIGHT_SCALE, 1f)
+                    .apply {
+                        duration = MENU_HEIGHT_ANIMATION_DURATION_MS
+                        interpolator = EMPHASIZED_DECELERATE
+                        addUpdateListener {
+                            // Animate padding so that controls stay pinned to the bottom of
+                            // the menu.
+                            val value = animatedValue as Float
+                            val topPadding = menuPadding -
+                                    ((1 - value) * menuHeight).toInt()
+                            rootView.setPadding(menuPadding, topPadding,
+                                menuPadding, menuPadding)
+                        }
+                    },
+                ValueAnimator.ofFloat(1 / STARTING_MENU_HEIGHT_SCALE, 1f).apply {
+                    duration = MENU_HEIGHT_ANIMATION_DURATION_MS
+                    interpolator = EMPHASIZED_DECELERATE
+                    addUpdateListener {
+                        // Scale up the children of the maximize menu so that the menu
+                        // scale is cancelled out and only the background is scaled.
+                        val value = animatedValue as Float
+                        maximizeButton.scaleY = value
+                        snapButtonsLayout.scaleY = value
+                        maximizeText.scaleY = value
+                        snapWindowText.scaleY = value
+                    }
+                },
+                ObjectAnimator.ofFloat(rootView, TRANSLATION_Y,
+                    (STARTING_MENU_HEIGHT_SCALE - 1) * menuHeight, 0f).apply {
+                    duration = MENU_HEIGHT_ANIMATION_DURATION_MS
+                    interpolator = EMPHASIZED_DECELERATE
+                },
+                ObjectAnimator.ofInt(rootView.background, "alpha",
+                    MAX_DRAWABLE_ALPHA_VALUE).apply {
+                    duration = ALPHA_ANIMATION_DURATION_MS
+                },
+                ValueAnimator.ofFloat(0f, 1f)
+                    .apply {
+                        duration = ALPHA_ANIMATION_DURATION_MS
+                        startDelay = CONTROLS_ALPHA_ANIMATION_DELAY_MS
+                        addUpdateListener {
+                            val value = animatedValue as Float
+                            maximizeButton.alpha = value
+                            snapButtonsLayout.alpha = value
+                            maximizeText.alpha = value
+                            snapWindowText.alpha = value
+                        }
+                    },
+                ObjectAnimator.ofFloat(rootView, TRANSLATION_Z, MENU_Z_TRANSLATION)
+                    .apply {
+                        duration = ELEVATION_ANIMATION_DURATION_MS
+                        startDelay = CONTROLS_ALPHA_ANIMATION_DELAY_MS
+                    }
+            )
+            openMenuAnimatorSet.addListener(
+                onEnd = {
+                    maximizeButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                    maximizeText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+                }
+            )
+            openMenuAnimatorSet.start()
+        }
+
+        /** Cancel the open menu animation. */
+        fun cancelAnimation() {
+            openMenuAnimatorSet.cancel()
+        }
+
+        /** Update the view state to a new snap to half selection. */
+        fun updateSplitSnapSelection(selection: SnapToHalfSelection) {
+            when (selection) {
+                SnapToHalfSelection.NONE -> deactivateSnapOptions()
+                SnapToHalfSelection.LEFT -> activateSnapOption(activateLeft = true)
+                SnapToHalfSelection.RIGHT -> activateSnapOption(activateLeft = false)
+            }
+        }
+
+        private fun calculateMenuStyle(taskInfo: RunningTaskInfo): MenuStyle {
+            val colorScheme = decorThemeUtil.getColorScheme(taskInfo)
+            val menuBackgroundColor = colorScheme.surfaceContainerLow.toArgb()
+            return MenuStyle(
+                backgroundColor = menuBackgroundColor,
+                textColor = colorScheme.onSurface.toArgb(),
+                maximizeOption = MenuStyle.MaximizeOption(
+                    drawable = createMaximizeDrawable(menuBackgroundColor, colorScheme)
+                ),
+                snapOptions = MenuStyle.SnapOptions(
+                    inactiveSnapSideColor = colorScheme.outlineVariant.toArgb(),
+                    semiActiveSnapSideColor = colorScheme.primary.toArgb().withAlpha(OPACITY_40),
+                    activeSnapSideColor = colorScheme.primary.toArgb(),
+                    inactiveStrokeColor = colorScheme.outlineVariant.toArgb(),
+                    activeStrokeColor = colorScheme.primary.toArgb(),
+                    inactiveBackgroundColor = menuBackgroundColor,
+                    activeBackgroundColor = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
+                ),
+            )
+        }
+
+        private fun deactivateSnapOptions() {
+            // TODO(b/346440693): the background/colorStateList set on these buttons is overridden
+            //  to a static resource & color on manually tracked hover events, which defeats the
+            //  point of state lists and selector states. Look into whether changing that is
+            //  possible, similar to the maximize option. Also to include support for the
+            //  semi-active state (when the "other" snap option is selected).
+            val snapSideColorList = ColorStateList(
+                arrayOf(
+                    intArrayOf(android.R.attr.state_pressed),
+                    intArrayOf(android.R.attr.state_focused),
+                    intArrayOf(android.R.attr.state_selected),
+                    intArrayOf(),
+                ),
+                intArrayOf(
+                    style.snapOptions.activeSnapSideColor,
+                    style.snapOptions.activeSnapSideColor,
+                    style.snapOptions.activeSnapSideColor,
+                    style.snapOptions.inactiveSnapSideColor
+                )
+            )
+            snapLeftButton.background?.setTintList(snapSideColorList)
+            snapRightButton.background?.setTintList(snapSideColorList)
+            with (snapButtonsLayout) {
+                setBackgroundResource(R.drawable.desktop_mode_maximize_menu_layout_background)
+                (background as GradientDrawable).apply {
+                    setColor(style.snapOptions.inactiveBackgroundColor)
+                    setStroke(outlineStroke, style.snapOptions.inactiveStrokeColor)
+                }
+            }
+        }
+
+        private fun activateSnapOption(activateLeft: Boolean) {
+            // Regardless of which side is active, the background of the snap options layout (that
+            // includes both sides) is considered "active".
+            with (snapButtonsLayout) {
+                setBackgroundResource(
+                    R.drawable.desktop_mode_maximize_menu_layout_background_on_hover)
+                (background as GradientDrawable).apply {
+                    setColor(style.snapOptions.activeBackgroundColor)
+                    setStroke(outlineStroke, style.snapOptions.activeStrokeColor)
+                }
+            }
+            if (activateLeft) {
+                // Highlight snap left button, partially highlight the other side.
+                snapLeftButton.background.setTint(style.snapOptions.activeSnapSideColor)
+                snapRightButton.background.setTint(style.snapOptions.semiActiveSnapSideColor)
+            } else {
+                // Highlight snap right button, partially highlight the other side.
+                snapRightButton.background.setTint(style.snapOptions.activeSnapSideColor)
+                snapLeftButton.background.setTint(style.snapOptions.semiActiveSnapSideColor)
+            }
+        }
+
+        private fun createMaximizeDrawable(
+            @ColorInt menuBackgroundColor: Int,
+            colorScheme: ColorScheme
+        ): StateListDrawable {
+            val activeStrokeAndFill = colorScheme.primary.toArgb()
+            val activeBackground = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
+            val activeDrawable = createMaximizeButtonDrawable(
+                strokeAndFillColor = activeStrokeAndFill,
+                backgroundColor = activeBackground,
+                // Add a mask with the menu background's color because the active background color is
+                // semi transparent, otherwise the transparency will reveal the stroke/fill color
+                // behind it.
+                backgroundMask = menuBackgroundColor
+            )
+            return StateListDrawable().apply {
+                addState(intArrayOf(android.R.attr.state_pressed), activeDrawable)
+                addState(intArrayOf(android.R.attr.state_focused), activeDrawable)
+                addState(intArrayOf(android.R.attr.state_selected), activeDrawable)
+                addState(intArrayOf(android.R.attr.state_hovered), activeDrawable)
+                // Inactive drawable.
+                addState(
+                    StateSet.WILD_CARD,
+                    createMaximizeButtonDrawable(
+                        strokeAndFillColor = colorScheme.outlineVariant.toArgb(),
+                        backgroundColor = colorScheme.surfaceContainerLow.toArgb(),
+                        backgroundMask = null // not needed because the bg color is fully opaque
+                    )
+                )
+            }
+        }
+
+        private fun createMaximizeButtonDrawable(
+            @ColorInt strokeAndFillColor: Int,
+            @ColorInt backgroundColor: Int,
+            @ColorInt backgroundMask: Int?
+        ): LayerDrawable {
+            val layers = mutableListOf<Drawable>()
+            // First (bottom) layer, effectively the button's border ring once its inner shape is
+            // covered by the next layers.
+            layers.add(ShapeDrawable().apply {
+                shape = RoundRectShape(
+                    FloatArray(8) { outlineRadius.toFloat() },
+                    null /* inset */,
+                    null /* innerRadii */
+                )
+                paint.color = strokeAndFillColor
+                paint.style = Paint.Style.FILL
+            })
+            // Second layer, a mask for the next (background) layer if needed because of
+            // transparency.
+            backgroundMask?.let { color ->
+                layers.add(
+                    ShapeDrawable().apply {
+                        shape = RoundRectShape(
+                            FloatArray(8) { outlineRadius.toFloat() },
+                            null /* inset */,
+                            null /* innerRadii */
+                        )
+                        paint.color = color
+                        paint.style = Paint.Style.FILL
+                    }
+                )
+            }
+            // Third layer, the "background" padding between the border and the fill.
+            layers.add(ShapeDrawable().apply {
+                shape = RoundRectShape(
+                    FloatArray(8) { outlineRadius.toFloat() },
+                    null /* inset */,
+                    null /* innerRadii */
+                )
+                paint.color = backgroundColor
+                paint.style = Paint.Style.FILL
+            })
+            // Final layer, the inner most rounded-rect "fill".
+            layers.add(ShapeDrawable().apply {
+                shape = RoundRectShape(
+                    FloatArray(8) { fillRadius.toFloat() },
+                    null /* inset */,
+                    null /* innerRadii */
+                )
+                paint.color = strokeAndFillColor
+                paint.style = Paint.Style.FILL
+            })
+            return LayerDrawable(layers.toTypedArray()).apply {
+                when (numberOfLayers) {
+                    3 -> {
+                        setLayerInset(1, outlineStroke)
+                        setLayerInset(2, fillPadding)
+                    }
+                    4 -> {
+                        setLayerInset(intArrayOf(1, 2), outlineStroke)
+                        setLayerInset(3, fillPadding)
+                    }
+                    else -> error("Unexpected number of layers: $numberOfLayers")
+                }
+            }
+        }
+
+        private fun LayerDrawable.setLayerInset(index: IntArray, inset: Int) {
+            for (i in index) {
+                setLayerInset(i, inset, inset, inset, inset)
+            }
+        }
+
+        private fun LayerDrawable.setLayerInset(index: Int, inset: Int) {
+            setLayerInset(index, inset, inset, inset, inset)
+        }
+
+        private fun requireViewById(id: Int) = rootView.requireViewById<View>(id)
+
+        /** The style to apply to the menu. */
+        data class MenuStyle(
+            @ColorInt val backgroundColor: Int,
+            @ColorInt val textColor: Int,
+            val maximizeOption: MaximizeOption,
+            val snapOptions: SnapOptions,
+        ) {
+            data class MaximizeOption(
+                val drawable: StateListDrawable,
+            )
+            data class SnapOptions(
+                @ColorInt val inactiveSnapSideColor: Int,
+                @ColorInt val semiActiveSnapSideColor: Int,
+                @ColorInt val activeSnapSideColor: Int,
+                @ColorInt val inactiveStrokeColor: Int,
+                @ColorInt val activeStrokeColor: Int,
+                @ColorInt val inactiveBackgroundColor: Int,
+                @ColorInt val activeBackgroundColor: Int,
+            )
+        }
+
+        /** The possible selection states of the half-snap menu option. */
+        enum class SnapToHalfSelection {
+            NONE, LEFT, RIGHT
         }
     }
 
@@ -352,7 +641,6 @@
         fun isMaximizeMenuView(@IdRes viewId: Int): Boolean {
             return viewId == R.id.maximize_menu ||
                     viewId == R.id.maximize_menu_maximize_button ||
-                    viewId == R.id.maximize_menu_maximize_button_layout ||
                     viewId == R.id.maximize_menu_snap_left_button ||
                     viewId == R.id.maximize_menu_snap_right_button ||
                     viewId == R.id.maximize_menu_snap_menu_layout ||
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
index 4f2d945..cd2dac8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
@@ -20,7 +20,6 @@
 import android.animation.ValueAnimator
 import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
-import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.Color
 import android.graphics.PixelFormat
@@ -36,10 +35,15 @@
 import android.view.WindowlessWindowManager
 import android.widget.ImageView
 import android.window.TaskConstants
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.ui.graphics.toArgb
 import com.android.wm.shell.R
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
 import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
+import com.android.wm.shell.windowdecor.common.Theme
 import java.util.function.Supplier
 
 /**
@@ -49,14 +53,18 @@
         private val context: Context,
         private val displayController: DisplayController,
         private val appIcon: Bitmap,
-        private val taskInfo: RunningTaskInfo,
         private var parentSurface: SurfaceControl,
         private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
         private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory =
                 object : SurfaceControlBuilderFactory {},
         private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory =
-                object : SurfaceControlViewHostFactory {}
+                object : SurfaceControlViewHostFactory {},
+        taskInfo: RunningTaskInfo,
 ) {
+    private val decorThemeUtil = DecorThemeUtil(context)
+    private val lightColors = dynamicLightColorScheme(context)
+    private val darkColors = dynamicDarkColorScheme(context)
+
     private val surfaceSession = SurfaceSession()
     private lateinit var iconView: ImageView
     private var iconSize = 0
@@ -86,21 +94,10 @@
                         return
                     }
                     displayController.removeDisplayWindowListener(this)
-                    setupResizeVeil()
+                    setupResizeVeil(taskInfo)
                 }
             }
 
-    private val backgroundColorId: Int
-        get() {
-            val configuration = context.resources.configuration
-            return if (configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
-                    == Configuration.UI_MODE_NIGHT_YES) {
-                R.color.desktop_mode_resize_veil_dark
-            } else {
-                R.color.desktop_mode_resize_veil_light
-            }
-        }
-
     /**
      * Whether the resize veil is ready to be shown.
      */
@@ -108,14 +105,14 @@
         get() = viewHost != null
 
     init {
-        setupResizeVeil()
+        setupResizeVeil(taskInfo)
     }
 
     /**
      * Create the veil in its default invisible state.
      */
-    private fun setupResizeVeil() {
-        if (!obtainDisplayOrRegisterListener()) {
+    private fun setupResizeVeil(taskInfo: RunningTaskInfo) {
+        if (!obtainDisplayOrRegisterListener(taskInfo.displayId)) {
             // Display may not be available yet, skip this until then.
             return
         }
@@ -162,8 +159,8 @@
         Trace.endSection()
     }
 
-    private fun obtainDisplayOrRegisterListener(): Boolean {
-        display = displayController.getDisplay(taskInfo.displayId)
+    private fun obtainDisplayOrRegisterListener(displayId: Int): Boolean {
+        display = displayController.getDisplay(displayId)
         if (display == null) {
             displayController.addDisplayWindowListener(onDisplaysChangedListener)
             return false
@@ -184,7 +181,8 @@
             t: SurfaceControl.Transaction,
             parent: SurfaceControl,
             taskBounds: Rect,
-            fadeIn: Boolean
+            taskInfo: RunningTaskInfo,
+            fadeIn: Boolean,
     ) {
         if (!isReady || isVisible) {
             t.apply()
@@ -202,13 +200,15 @@
             parentSurface = parent
         }
 
-
+        val backgroundColor = when (decorThemeUtil.getAppTheme(taskInfo)) {
+            Theme.LIGHT -> lightColors.surfaceContainer
+            Theme.DARK -> darkColors.surfaceContainer
+        }
         t.show(veil)
                 .setLayer(veil, VEIL_CONTAINER_LAYER)
                 .setLayer(icon, VEIL_ICON_LAYER)
                 .setLayer(background, VEIL_BACKGROUND_LAYER)
-                .setColor(background,
-                        Color.valueOf(context.getColor(backgroundColorId)).components)
+                .setColor(background, Color.valueOf(backgroundColor.toArgb()).components)
         relayout(taskBounds, t)
         if (fadeIn) {
             cancelAnimation()
@@ -270,12 +270,12 @@
     /**
      * Animate veil's alpha to 1, fading it in.
      */
-    fun showVeil(parentSurface: SurfaceControl, taskBounds: Rect) {
+    fun showVeil(parentSurface: SurfaceControl, taskBounds: Rect, taskInfo: RunningTaskInfo) {
         if (!isReady || isVisible) {
             return
         }
         val t = surfaceControlTransactionSupplier.get()
-        showVeil(t, parentSurface, taskBounds, true /* fadeIn */)
+        showVeil(t, parentSurface, taskBounds, taskInfo, true /* fadeIn */)
     }
 
     /**
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 a08f97c..216990c 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
@@ -199,8 +199,16 @@
     void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
             SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
             RelayoutResult<T> outResult) {
-        outResult.reset();
+        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;
         }
@@ -213,13 +221,38 @@
             return;
         }
 
+        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, mTaskInfo.displayId);
+
+        final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
+        outResult.mWidth = taskBounds.width();
+        outResult.mHeight = taskBounds.height();
+        outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
+        final Resources resources = mDecorWindowContext.getResources();
+        outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
+        outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL
+                ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
+        outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
+
+        updateDecorationContainerSurface(startT, outResult);
+        updateCaptionContainerSurface(startT, outResult);
+        updateCaptionInsets(params, wct, outResult, taskBounds);
+        updateTaskSurface(params, startT, finishT, outResult);
+    }
+
+    private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct,
+            T rootView, int oldLayoutResId, RelayoutResult<T> outResult) {
         if (rootView == null && params.mLayoutResId == 0) {
             throw new IllegalArgumentException("layoutResId and rootView can't both be invalid.");
         }
 
         outResult.mRootView = rootView;
-        rootView = null; // Clear it just in case we use it accidentally
-
         final int oldDensityDpi = mWindowDecorConfig != null
                 ? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED;
         final int oldNightMode =  mWindowDecorConfig != null
@@ -253,25 +286,17 @@
             outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
                     .inflate(params.mLayoutResId, null);
         }
+    }
 
-        updateCaptionVisibility(outResult.mRootView, mTaskInfo.displayId);
-
-        final Resources resources = mDecorWindowContext.getResources();
-        final Configuration taskConfig = mTaskInfo.getConfiguration();
-        final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
-        final boolean isFullscreen = taskConfig.windowConfiguration.getWindowingMode()
-                == WINDOWING_MODE_FULLSCREEN;
-        outResult.mWidth = taskBounds.width();
-        outResult.mHeight = taskBounds.height();
-
-        // DecorationContainerSurface
+    private void updateDecorationContainerSurface(
+            SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
         if (mDecorationContainerSurface == null) {
             final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
             mDecorationContainerSurface = builder
                     .setName("Decor container of Task=" + mTaskInfo.taskId)
                     .setContainerLayer()
                     .setParent(mTaskSurface)
-                    .setCallsite("WindowDecoration.relayout_1")
+                    .setCallsite("WindowDecoration.updateDecorationContainerSurface")
                     .build();
 
             startT.setTrustedOverlay(mDecorationContainerSurface, true)
@@ -281,101 +306,101 @@
 
         startT.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight)
                 .show(mDecorationContainerSurface);
+    }
 
-        // CaptionContainerSurface, CaptionWindowManager
+    private void updateCaptionContainerSurface(
+            SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
         if (mCaptionContainerSurface == null) {
             final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
             mCaptionContainerSurface = builder
                     .setName("Caption container of Task=" + mTaskInfo.taskId)
                     .setContainerLayer()
                     .setParent(mDecorationContainerSurface)
-                    .setCallsite("WindowDecoration.relayout_2")
+                    .setCallsite("WindowDecoration.updateCaptionContainerSurface")
                     .build();
         }
 
-        outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
-        outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL
-                ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
-        outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
-
         startT.setWindowCrop(mCaptionContainerSurface, outResult.mCaptionWidth,
                         outResult.mCaptionHeight)
                 .setPosition(mCaptionContainerSurface, outResult.mCaptionX, 0 /* y */)
                 .setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
                 .show(mCaptionContainerSurface);
+    }
 
-        outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
-
-        // Caption insets
-        if (mIsCaptionVisible) {
-            // Caption inset is the full width of the task with the |captionHeight| and
-            // positioned at the top of the task bounds, also in absolute coordinates.
-            // So just reuse the task bounds and adjust the bottom coordinate.
-            final Rect captionInsetsRect = new Rect(taskBounds);
-            captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight;
-
-            // Caption bounding rectangles: these are optional, and are used to present finer
-            // insets than traditional |Insets| to apps about where their content is occluded.
-            // These are also in absolute coordinates.
-            final Rect[] boundingRects;
-            final int numOfElements = params.mOccludingCaptionElements.size();
-            if (numOfElements == 0) {
-                boundingRects = null;
-            } else {
-                // The customizable region can at most be equal to the caption bar.
-                if (params.hasInputFeatureSpy()) {
-                    outResult.mCustomizableCaptionRegion.set(captionInsetsRect);
-                }
-                boundingRects = new Rect[numOfElements];
-                for (int i = 0; i < numOfElements; i++) {
-                    final OccludingCaptionElement element =
-                            params.mOccludingCaptionElements.get(i);
-                    final int elementWidthPx =
-                            resources.getDimensionPixelSize(element.mWidthResId);
-                    boundingRects[i] =
-                            calculateBoundingRect(element, elementWidthPx, captionInsetsRect);
-                    // Subtract the regions used by the caption elements, the rest is
-                    // customizable.
-                    if (params.hasInputFeatureSpy()) {
-                        outResult.mCustomizableCaptionRegion.op(boundingRects[i],
-                                Region.Op.DIFFERENCE);
-                    }
-                }
-            }
-
-            final WindowDecorationInsets newInsets = new WindowDecorationInsets(
-                    mTaskInfo.token, mOwner, captionInsetsRect, boundingRects);
-            if (!newInsets.equals(mWindowDecorationInsets)) {
-                // Add or update this caption as an insets source.
-                mWindowDecorationInsets = newInsets;
-                mWindowDecorationInsets.addOrUpdate(wct);
-            }
-        } else {
+    private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
+            RelayoutResult<T> outResult, Rect taskBounds) {
+        if (!mIsCaptionVisible) {
             if (mWindowDecorationInsets != null) {
                 mWindowDecorationInsets.remove(wct);
                 mWindowDecorationInsets = null;
             }
+            return;
         }
+        // Caption inset is the full width of the task with the |captionHeight| and
+        // positioned at the top of the task bounds, also in absolute coordinates.
+        // So just reuse the task bounds and adjust the bottom coordinate.
+        final Rect captionInsetsRect = new Rect(taskBounds);
+        captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight;
 
-        // Task surface itself
-        float shadowRadius;
-        final Point taskPosition = mTaskInfo.positionInParent;
-        if (isFullscreen) {
-            // Shadow is not needed for fullscreen tasks
-            shadowRadius = 0;
+        // Caption bounding rectangles: these are optional, and are used to present finer
+        // insets than traditional |Insets| to apps about where their content is occluded.
+        // These are also in absolute coordinates.
+        final Rect[] boundingRects;
+        final int numOfElements = params.mOccludingCaptionElements.size();
+        if (numOfElements == 0) {
+            boundingRects = null;
         } else {
-            shadowRadius = loadDimension(resources, params.mShadowRadiusId);
+            // The customizable region can at most be equal to the caption bar.
+            if (params.hasInputFeatureSpy()) {
+                outResult.mCustomizableCaptionRegion.set(captionInsetsRect);
+            }
+            final Resources resources = mDecorWindowContext.getResources();
+            boundingRects = new Rect[numOfElements];
+            for (int i = 0; i < numOfElements; i++) {
+                final OccludingCaptionElement element =
+                        params.mOccludingCaptionElements.get(i);
+                final int elementWidthPx =
+                        resources.getDimensionPixelSize(element.mWidthResId);
+                boundingRects[i] =
+                        calculateBoundingRect(element, elementWidthPx, captionInsetsRect);
+                // Subtract the regions used by the caption elements, the rest is
+                // customizable.
+                if (params.hasInputFeatureSpy()) {
+                    outResult.mCustomizableCaptionRegion.op(boundingRects[i],
+                            Region.Op.DIFFERENCE);
+                }
+            }
         }
 
+        final WindowDecorationInsets newInsets = new WindowDecorationInsets(
+                mTaskInfo.token, mOwner, captionInsetsRect, boundingRects);
+        if (!newInsets.equals(mWindowDecorationInsets)) {
+            // Add or update this caption as an insets source.
+            mWindowDecorationInsets = newInsets;
+            mWindowDecorationInsets.addOrUpdate(wct);
+        }
+    }
+
+    private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT,
+            SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) {
         if (params.mSetTaskPositionAndCrop) {
+            final Point taskPosition = mTaskInfo.positionInParent;
             startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
             finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
                     .setPosition(mTaskSurface, taskPosition.x, taskPosition.y);
         }
 
-        startT.setShadowRadius(mTaskSurface, shadowRadius)
-                .show(mTaskSurface);
+        float shadowRadius;
+        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+            // Shadow is not needed for fullscreen tasks
+            shadowRadius = 0;
+        } else {
+            shadowRadius =
+                    loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId);
+        }
+        startT.setShadowRadius(mTaskSurface, shadowRadius).show(mTaskSurface);
         finishT.setShadowRadius(mTaskSurface, shadowRadius);
+
         if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
             if (!DesktopModeStatus.isVeiledResizeEnabled()) {
                 // When fluid resize is enabled, add a background to freeform tasks
@@ -390,7 +415,19 @@
         } else if (!DesktopModeStatus.isVeiledResizeEnabled()) {
             startT.unsetColor(mTaskSurface);
         }
+    }
 
+    /**
+     * 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
@@ -399,9 +436,7 @@
                     mTaskInfo.getConfiguration(), mCaptionContainerSurface,
                     null /* hostInputToken */);
         }
-
-        // Caption view
-        mCaptionWindowManager.setConfiguration(taskConfig);
+        mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration());
         final WindowManager.LayoutParams lp =
                 new WindowManager.LayoutParams(outResult.mCaptionWidth, outResult.mCaptionHeight,
                         TYPE_APPLICATION,
@@ -414,14 +449,20 @@
             mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
                     mCaptionWindowManager);
             if (params.mApplyStartTransactionOnDraw) {
-                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+                if (onDrawTransaction == null) {
+                    throw new IllegalArgumentException("Trying to sync a null Transaction");
+                }
+                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
             }
             mViewHost.setView(outResult.mRootView, lp);
             Trace.endSection();
         } else {
             Trace.beginSection("CaptionViewHostLayout-relayout");
             if (params.mApplyStartTransactionOnDraw) {
-                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(startT);
+                if (onDrawTransaction == null) {
+                    throw new IllegalArgumentException("Trying to sync a null Transaction");
+                }
+                mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction);
             }
             mViewHost.relayout(lp);
             Trace.endSection();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
index 8e6d1ee9..f7cfbfa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
@@ -15,11 +15,16 @@
  */
 package com.android.wm.shell.windowdecor.common
 
+import android.annotation.ColorInt
+import android.annotation.IntRange
 import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
 import android.content.res.Configuration
 import android.content.res.Configuration.UI_MODE_NIGHT_MASK
 import android.graphics.Color
+import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
 
 /** The theme of a window decoration. */
 internal enum class Theme { LIGHT, DARK }
@@ -30,10 +35,31 @@
 /** Whether a [Theme] is dark. */
 internal fun Theme.isDark(): Boolean = this == Theme.DARK
 
+/** Returns a copy of the color with its [alpha] component replaced with the given value. */
+@ColorInt
+internal fun @receiver:ColorInt Int.withAlpha(@IntRange(from = 0, to = 255) alpha: Int): Int =
+    Color.argb(
+        alpha,
+        Color.red(this),
+        Color.green(this),
+        Color.blue(this)
+    )
+
+/** Common opacity values used in window decoration views. */
+const val OPACITY_100 = 255
+const val OPACITY_11 = 28
+const val OPACITY_12 = 31
+const val OPACITY_15 = 38
+const val OPACITY_40 = 102
+const val OPACITY_55 = 140
+const val OPACITY_65 = 166
+
 /**
  * Utility class for determining themes based on system settings and app's [RunningTaskInfo].
  */
 internal class DecorThemeUtil(private val context: Context) {
+    private val lightColors = dynamicLightColorScheme(context)
+    private val darkColors = dynamicDarkColorScheme(context)
 
     private val systemTheme: Theme
         get() = if ((context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK) ==
@@ -56,4 +82,13 @@
             Theme.LIGHT
         }
     }
+
+    /**
+     * Returns the [ColorScheme] to use to style window decorations based on the given
+     * [RunningTaskInfo].
+     */
+    fun getColorScheme(task: RunningTaskInfo): ColorScheme = when (getAppTheme(task)) {
+        Theme.LIGHT -> lightColors
+        Theme.DARK -> darkColors
+    }
 }
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 0650154..46127b1 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
@@ -45,6 +45,11 @@
 import com.android.wm.shell.R
 import com.android.wm.shell.windowdecor.MaximizeButtonView
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
+import com.android.wm.shell.windowdecor.common.OPACITY_100
+import com.android.wm.shell.windowdecor.common.OPACITY_11
+import com.android.wm.shell.windowdecor.common.OPACITY_15
+import com.android.wm.shell.windowdecor.common.OPACITY_55
+import com.android.wm.shell.windowdecor.common.OPACITY_65
 import com.android.wm.shell.windowdecor.common.Theme
 import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance
 import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppearance
@@ -491,11 +496,5 @@
         private const val DARK_THEME_UNFOCUSED_OPACITY = 140 // 55%
         private const val LIGHT_THEME_UNFOCUSED_OPACITY = 166 // 65%
         private const val FOCUSED_OPACITY = 255
-
-        private const val OPACITY_100 = 255
-        private const val OPACITY_11 = 28
-        private const val OPACITY_15 = 38
-        private const val OPACITY_55 = 140
-        private const val OPACITY_65 = 166
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
index f813b0d..0fe7a16b 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
@@ -65,9 +65,11 @@
 
 android_test {
     name: "WMShellFlickerTestsSplitScreenGroup2",
+    defaults: ["WMShellFlickerTestsDefault"],
     manifest: "AndroidManifest.xml",
     package_name: "com.android.wm.shell.flicker.splitscreen",
     instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    test_config_template: "AndroidTestTemplate.xml",
     srcs: [
         ":WMShellFlickerTestsSplitScreenBase-src",
         ":WMShellFlickerTestsSplitScreenGroup2-src",
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
new file mode 100644
index 0000000..dad5db9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.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.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.traces.component.ComponentNameMatcher
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitscreen.benchmark.MultipleShowImeRequestsInSplitScreenBenchmark
+import com.android.wm.shell.flicker.utils.ICommonAssertions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switch between two split pairs.
+ *
+ * To run this test: `atest WMShellFlickerTestsSplitScreenGroup2:MultipleShowImeRequestsInSplitScreen`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class MultipleShowImeRequestsInSplitScreen(override val flicker: LegacyFlickerTest) :
+        MultipleShowImeRequestsInSplitScreenBenchmark(flicker), ICommonAssertions {
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            defaultSetup(this)
+            defaultTeardown(this)
+            thisTransition(this)
+        }
+
+    @Presubmit
+    @Test
+    fun imeLayerAlwaysVisible() =
+            flicker.assertLayers {
+                this.isVisible(ComponentNameMatcher.IME)
+            }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() = LegacyFlickerTestFactory.nonRotationTests(
+                supportedRotations = listOf(Rotation.ROTATION_0)
+        )
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index 9045364..d349988 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -47,7 +47,7 @@
  * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen`
  */
 @RequiresDevice
-@Postsubmit
+@FlakyTest(bugId = 293578017)
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -61,7 +61,6 @@
         }
 
     @Test
-    @FlakyTest(bugId = 293578017)
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
         super.visibleLayersShownMoreThanOneConsecutiveEntry()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
new file mode 100644
index 0000000..2492531
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.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.wm.shell.flicker.splitscreen.benchmark
+
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+abstract class MultipleShowImeRequestsInSplitScreenBenchmark(
+        override val flicker: LegacyFlickerTest
+) : SplitScreenBase(flicker) {
+    override val primaryApp = ImeAppHelper(instrumentation)
+    override val defaultTeardown: FlickerBuilder.() -> Unit
+        get() = {
+            teardown {
+                primaryApp.closeIME(wmHelper)
+                super.defaultTeardown
+            }
+        }
+
+    protected val thisTransition: FlickerBuilder.() -> Unit
+        get() = {
+            setup {
+                SplitScreenUtils.enterSplit(
+                        wmHelper,
+                        tapl,
+                        device,
+                        primaryApp,
+                        secondaryApp,
+                        flicker.scenario.startRotation
+                )
+                // initially open the IME
+                primaryApp.openIME(wmHelper)
+            }
+            transitions {
+                for (i in 1..OPEN_IME_COUNT) {
+                    primaryApp.openIME(wmHelper)
+                }
+            }
+        }
+
+    companion object {
+        const val OPEN_IME_COUNT = 30
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
index 4b10603..51074f6 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
@@ -25,7 +25,7 @@
 
 abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) {
     protected val context: Context = instrumentation.context
-    protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    protected open val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
     protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
 
     protected open val defaultSetup: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index bd20c11..731f75bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -20,9 +20,11 @@
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
 
+import static com.android.wm.shell.activityembedding.ActivityEmbeddingAnimationRunner.calculateParentBounds;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_TASK_FRAGMENT_DRAG_RESIZE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
@@ -32,6 +34,9 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.animation.Animator;
+import android.annotation.NonNull;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -130,7 +135,7 @@
     @Test
     public void testInvalidCustomAnimation_disableAnimationOptionsPerChange() {
         final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY))
+                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, TRANSIT_OPEN))
                 .build();
         info.setAnimationOptions(TransitionInfo.AnimationOptions
                 .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
@@ -148,7 +153,7 @@
     @Test
     public void testInvalidCustomAnimation_enableAnimationOptionsPerChange() {
         final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY))
+                .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, TRANSIT_OPEN))
                 .build();
         info.getChanges().getFirst().setAnimationOptions(TransitionInfo.AnimationOptions
                 .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
@@ -161,4 +166,128 @@
         // An invalid custom animation is equivalent to jump-cut.
         assertEquals(0, animator.getDuration());
     }
+
+    @DisableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
+    @Test
+    public void testCalculateParentBounds_flagDisabled() {
+        final Rect parentBounds = new Rect(0, 0, 2000, 2000);
+        final Rect primaryBounds = new Rect();
+        final Rect secondaryBounds = new Rect();
+        parentBounds.splitVertically(primaryBounds, secondaryBounds);
+
+        final TransitionInfo.Change change = createChange(0 /* flags */);
+        change.setStartAbsBounds(secondaryBounds);
+
+        final TransitionInfo.Change boundsAnimationChange = createChange(0 /* flags */);
+        boundsAnimationChange.setStartAbsBounds(primaryBounds);
+        boundsAnimationChange.setEndAbsBounds(primaryBounds);
+        final Rect actualParentBounds = new Rect();
+
+        calculateParentBounds(change, boundsAnimationChange, actualParentBounds);
+
+        assertEquals(parentBounds, actualParentBounds);
+
+        actualParentBounds.setEmpty();
+
+        boundsAnimationChange.setStartAbsBounds(secondaryBounds);
+        boundsAnimationChange.setEndAbsBounds(primaryBounds);
+
+        calculateParentBounds(boundsAnimationChange, boundsAnimationChange, actualParentBounds);
+
+        assertEquals(parentBounds, actualParentBounds);
+    }
+
+    // TODO(b/243518738): Rewrite with TestParameter
+    @EnableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
+    @Test
+    public void testCalculateParentBounds_flagEnabled() {
+        TransitionInfo.Change change;
+        final TransitionInfo.Change stubChange = createChange(0 /* flags */);
+        final Rect actualParentBounds = new Rect();
+        Rect parentBounds = new Rect(0, 0, 2000, 2000);
+        Rect endAbsBounds = new Rect(0, 0, 2000, 2000);
+        change = prepareChangeForParentBoundsCalculationTest(
+                new Point(0, 0) /* endRelOffset */,
+                endAbsBounds,
+                new Point() /* endParentSize */
+        );
+
+        calculateParentBounds(change, stubChange, actualParentBounds);
+
+        assertTrue("Parent bounds must be empty because end parent size is not set.",
+                actualParentBounds.isEmpty());
+
+        String testString = "Parent start with (0, 0)";
+        change = prepareChangeForParentBoundsCalculationTest(
+                new Point(endAbsBounds.left - parentBounds.left,
+                        endAbsBounds.top - parentBounds.top),
+                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
+
+        calculateParentBounds(change, stubChange, actualParentBounds);
+
+        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
+                actualParentBounds);
+
+        testString = "Container not start with (0, 0)";
+        parentBounds = new Rect(0, 0, 2000, 2000);
+        endAbsBounds = new Rect(1000, 500, 2000, 1500);
+        change = prepareChangeForParentBoundsCalculationTest(
+                new Point(endAbsBounds.left - parentBounds.left,
+                        endAbsBounds.top - parentBounds.top),
+                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
+
+        calculateParentBounds(change, stubChange, actualParentBounds);
+
+        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
+                actualParentBounds);
+
+        testString = "Parent container on the right";
+        parentBounds = new Rect(1000, 0, 2000, 2000);
+        endAbsBounds = new Rect(1000, 500, 1500, 1500);
+        change = prepareChangeForParentBoundsCalculationTest(
+                new Point(endAbsBounds.left - parentBounds.left,
+                        endAbsBounds.top - parentBounds.top),
+                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
+
+        calculateParentBounds(change, stubChange, actualParentBounds);
+
+        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
+                actualParentBounds);
+
+        testString = "Parent container on the bottom";
+        parentBounds = new Rect(0, 1000, 2000, 2000);
+        endAbsBounds = new Rect(500, 1500, 1500, 2000);
+        change = prepareChangeForParentBoundsCalculationTest(
+                new Point(endAbsBounds.left - parentBounds.left,
+                        endAbsBounds.top - parentBounds.top),
+                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
+
+        calculateParentBounds(change, stubChange, actualParentBounds);
+
+        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
+                actualParentBounds);
+
+        testString = "Parent container in the middle";
+        parentBounds = new Rect(500, 500, 1500, 1500);
+        endAbsBounds = new Rect(1000, 500, 1500, 1000);
+        change = prepareChangeForParentBoundsCalculationTest(
+                new Point(endAbsBounds.left - parentBounds.left,
+                        endAbsBounds.top - parentBounds.top),
+                endAbsBounds, new Point(parentBounds.width(), parentBounds.height()));
+
+        calculateParentBounds(change, stubChange, actualParentBounds);
+
+        assertEquals(testString + ": Parent bounds must be " + parentBounds, parentBounds,
+                actualParentBounds);
+    }
+
+    @NonNull
+    private static TransitionInfo.Change prepareChangeForParentBoundsCalculationTest(
+            @NonNull Point endRelOffset, @NonNull Rect endAbsBounds, @NonNull Point endParentSize) {
+        final TransitionInfo.Change change = createChange(0 /* flags */);
+        change.setEndRelOffset(endRelOffset.x, endRelOffset.y);
+        change.setEndAbsBounds(endAbsBounds);
+        change.setEndParentSize(endParentSize.x, endParentSize.y);
+        return change;
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
index 0b2265d..c18d7ec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.activityembedding;
 
+import static android.view.WindowManager.TRANSIT_NONE;
 import static android.window.TransitionInfo.FLAG_FILLS_TASK;
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
 
@@ -31,6 +32,7 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.view.SurfaceControl;
+import android.view.WindowManager;
 import android.window.TransitionInfo;
 import android.window.WindowContainerToken;
 
@@ -82,11 +84,27 @@
         spyOn(mFinishCallback);
     }
 
-    /** Creates a mock {@link TransitionInfo.Change}. */
+    /**
+     * Creates a mock {@link TransitionInfo.Change}.
+     *
+     * @param flags the {@link TransitionInfo.ChangeFlags} of the change
+     */
     static TransitionInfo.Change createChange(@TransitionInfo.ChangeFlags int flags) {
+        return createChange(flags, TRANSIT_NONE);
+    }
+
+    /**
+     * Creates a mock {@link TransitionInfo.Change}.
+     *
+     * @param flags the {@link TransitionInfo.ChangeFlags} of the change
+     * @param mode the transition mode of the change
+     */
+    static TransitionInfo.Change createChange(@TransitionInfo.ChangeFlags int flags,
+            @WindowManager.TransitionType int mode) {
         TransitionInfo.Change c = new TransitionInfo.Change(mock(WindowContainerToken.class),
                 mock(SurfaceControl.class));
         c.setFlags(flags);
+        c.setMode(mode);
         return c;
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index f6f3aa4..57e469d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -123,6 +123,7 @@
     private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation;
     private CrossTaskBackAnimation mCrossTaskBackAnimation;
     private ShellBackAnimationRegistry mShellBackAnimationRegistry;
+    private Rect mTouchableRegion;
 
     @Before
     public void setUp() throws Exception {
@@ -158,6 +159,8 @@
                         mShellCommandHandler);
         mShellInit.init();
         mShellExecutor.flushAll();
+        mTouchableRegion = new Rect(0, 0, 100, 100);
+        mController.mTouchableArea.set(mTouchableRegion);
     }
 
     private void createNavigationInfo(int backType,
@@ -169,7 +172,8 @@
                         .setOnBackNavigationDone(new RemoteCallback((bundle) -> {}))
                         .setOnBackInvokedCallback(mAppCallback)
                         .setPrepareRemoteAnimation(enableAnimation)
-                        .setAnimationCallback(isAnimationCallback);
+                        .setAnimationCallback(isAnimationCallback)
+                        .setTouchableRegion(mTouchableRegion);
 
         createNavigationInfo(builder);
     }
@@ -234,7 +238,8 @@
                     .setType(type)
                     .setOnBackInvokedCallback(mAppCallback)
                     .setPrepareRemoteAnimation(true)
-                    .setOnBackNavigationDone(new RemoteCallback(result)));
+                    .setOnBackNavigationDone(new RemoteCallback(result))
+                    .setTouchableRegion(mTouchableRegion));
             triggerBackGesture();
             simulateRemoteAnimationStart();
             mShellExecutor.flushAll();
@@ -512,7 +517,8 @@
                     .setType(type)
                     .setOnBackInvokedCallback(mAppCallback)
                     .setPrepareRemoteAnimation(true)
-                    .setOnBackNavigationDone(new RemoteCallback(result)));
+                    .setOnBackNavigationDone(new RemoteCallback(result))
+                    .setTouchableRegion(mTouchableRegion));
             triggerBackGesture();
             simulateRemoteAnimationStart();
             mShellExecutor.flushAll();
@@ -543,7 +549,9 @@
         createNavigationInfo(new BackNavigationInfo.Builder()
                 .setType(type)
                 .setOnBackInvokedCallback(mAppCallback)
-                .setOnBackNavigationDone(new RemoteCallback(result)));
+                .setOnBackNavigationDone(new RemoteCallback(result))
+                .setTouchableRegion(mTouchableRegion)
+                .setAppProgressAllowed(true));
         triggerBackGesture();
         mShellExecutor.flushAll();
         releaseBackGesture();
@@ -570,7 +578,8 @@
         createNavigationInfo(new BackNavigationInfo.Builder()
                 .setType(type)
                 .setOnBackInvokedCallback(mAppCallback)
-                .setOnBackNavigationDone(new RemoteCallback(result)));
+                .setOnBackNavigationDone(new RemoteCallback(result))
+                .setTouchableRegion(mTouchableRegion));
         doMotionEvent(MotionEvent.ACTION_CANCEL, 0);
         mShellExecutor.flushAll();
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 8932e60..4d0348b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.os.Handler;
 import android.os.Looper;
@@ -95,16 +96,33 @@
         // Trigger animation cancel, the target progress should be 0.
         mTargetProgress = 0;
         mTargetProgressCalled = new CountDownLatch(1);
-        CountDownLatch cancelCallbackCalled = new CountDownLatch(1);
+        CountDownLatch finishCallbackCalled = new CountDownLatch(1);
         mMainThreadHandler.post(
-                () -> mProgressAnimator.onBackCancelled(() -> cancelCallbackCalled.countDown()));
-        cancelCallbackCalled.await(1, TimeUnit.SECONDS);
+                () -> mProgressAnimator.onBackCancelled(finishCallbackCalled::countDown));
+        finishCallbackCalled.await(1, TimeUnit.SECONDS);
         mTargetProgressCalled.await(1, TimeUnit.SECONDS);
         assertNotNull(mReceivedBackEvent);
         assertEquals(mReceivedBackEvent.getProgress(), mTargetProgress, 0 /* delta */);
     }
 
     @Test
+    public void testBackInvoked() throws InterruptedException {
+        // Give the animator some progress.
+        final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress);
+        mMainThreadHandler.post(
+                () -> mProgressAnimator.onBackProgressed(backEvent));
+        mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+        assertNotNull(mReceivedBackEvent);
+
+        // Trigger back invoked animation
+        CountDownLatch finishCallbackCalled = new CountDownLatch(1);
+        mMainThreadHandler.post(
+                () -> mProgressAnimator.onBackInvoked(finishCallbackCalled::countDown));
+        assertTrue("onBackInvoked finishCallback never called",
+                finishCallbackCalled.await(1, TimeUnit.SECONDS));
+    }
+
+    @Test
     public void testResetCallsCancelCallbackImmediately() throws InterruptedException {
         // Give the animator some progress.
         final BackMotionEvent backEvent = backMotionEventFrom(100, mTargetProgress);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 0f43377..93e4051 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -1170,9 +1170,9 @@
         // Verify the update has the removals.
         BubbleData.Update update = mUpdateCaptor.getValue();
         assertThat(update.removedBubbles.get(0)).isEqualTo(
-                Pair.create(mBubbleA2, Bubbles.DISMISS_USER_REMOVED));
+                Pair.create(mBubbleA2, Bubbles.DISMISS_USER_ACCOUNT_REMOVED));
         assertThat(update.removedBubbles.get(1)).isEqualTo(
-                Pair.create(mBubbleA1, Bubbles.DISMISS_USER_REMOVED));
+                Pair.create(mBubbleA1, Bubbles.DISMISS_USER_ACCOUNT_REMOVED));
 
         // Verify no A bubbles in active or overflow.
         assertBubbleListContains(mBubbleC1, mBubbleB3);
@@ -1203,6 +1203,25 @@
         assertThat(update.currentBubbleList.get(0).getKey()).isEqualTo(mEntryA2.getKey());
         assertThat(update.currentBubbleList.get(1).getKey()).isEqualTo(mEntryA1.getKey());
         assertThat(update.bubbleBarLocation).isEqualTo(BubbleBarLocation.LEFT);
+        assertThat(update.expandedChanged).isFalse();
+        assertThat(update.selectedBubbleKey).isEqualTo(mEntryA2.getKey());
+    }
+
+    @Test
+    public void test_getInitialStateForBubbleBar_includesExpandedState() {
+        sendUpdatedEntryAtTime(mEntryA1, 1000);
+        sendUpdatedEntryAtTime(mEntryA2, 2000);
+        mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT);
+        mBubbleData.setExpanded(true);
+
+        BubbleBarUpdate update = mBubbleData.getInitialStateForBubbleBar();
+        assertThat(update.currentBubbleList).hasSize(2);
+        assertThat(update.currentBubbleList.get(0).getKey()).isEqualTo(mEntryA2.getKey());
+        assertThat(update.currentBubbleList.get(1).getKey()).isEqualTo(mEntryA1.getKey());
+        assertThat(update.bubbleBarLocation).isEqualTo(BubbleBarLocation.LEFT);
+        assertThat(update.expandedChanged).isTrue();
+        assertThat(update.expanded).isTrue();
+        assertThat(update.selectedBubbleKey).isEqualTo(mEntryA2.getKey());
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 7122181..fb03f20 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -22,6 +22,7 @@
 import android.os.IBinder
 import android.testing.AndroidTestingRunner
 import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_CLOSE
 import android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS
 import android.view.WindowManager.TRANSIT_NONE
@@ -40,14 +41,14 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
-import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
+import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
 import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.TransitionInfoBuilder
@@ -78,410 +79,559 @@
 @RunWith(AndroidTestingRunner::class)
 class DesktopModeLoggerTransitionObserverTest {
 
-    @JvmField
-    @Rule
-    val extendedMockitoRule =
-        ExtendedMockitoRule.Builder(this)
-            .mockStatic(DesktopModeEventLogger::class.java)
-            .mockStatic(DesktopModeStatus::class.java)
-            .build()!!
+  @JvmField
+  @Rule
+  val extendedMockitoRule =
+      ExtendedMockitoRule.Builder(this)
+          .mockStatic(DesktopModeEventLogger::class.java)
+          .mockStatic(DesktopModeStatus::class.java)
+          .build()!!
 
-    @Mock lateinit var testExecutor: ShellExecutor
-    @Mock private lateinit var mockShellInit: ShellInit
-    @Mock private lateinit var transitions: Transitions
-    @Mock private lateinit var context: Context
+  @Mock lateinit var testExecutor: ShellExecutor
+  @Mock private lateinit var mockShellInit: ShellInit
+  @Mock private lateinit var transitions: Transitions
+  @Mock private lateinit var context: Context
 
-    private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
-    private lateinit var shellInit: ShellInit
-    private lateinit var desktopModeEventLogger: DesktopModeEventLogger
+  private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver
+  private lateinit var shellInit: ShellInit
+  private lateinit var desktopModeEventLogger: DesktopModeEventLogger
 
-    @Before
-    fun setup() {
-        doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
-        shellInit = Mockito.spy(ShellInit(testExecutor))
-        desktopModeEventLogger = mock(DesktopModeEventLogger::class.java)
+  @Before
+  fun setup() {
+    doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) }
+    shellInit = Mockito.spy(ShellInit(testExecutor))
+    desktopModeEventLogger = mock(DesktopModeEventLogger::class.java)
 
-        transitionObserver =
-            DesktopModeLoggerTransitionObserver(
-                context,
-                mockShellInit,
-                transitions,
-                desktopModeEventLogger
-            )
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
-            verify(mockShellInit)
-                .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
-            initRunnableCaptor.value.run()
-        } else {
-            transitionObserver.onInit()
-        }
+    transitionObserver =
+        DesktopModeLoggerTransitionObserver(
+            context, mockShellInit, transitions, desktopModeEventLogger)
+    if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+      val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+      verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
+      initRunnableCaptor.value.run()
+    } else {
+      transitionObserver.onInit()
+    }
+  }
+
+  @Test
+  fun testRegistersObserverAtInit() {
+    verify(transitions).registerObserver(same(transitionObserver))
+  }
+
+  @Test
+  fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
+    val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
+    verify(desktopModeEventLogger, never()).logTaskAdded(any(), any())
+  }
+
+  @Test
+  fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
+    val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    // task change is finalised when drag ends
+    val transitionInfo =
+        TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
+            .addChange(change)
+            .build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
+            .addChange(change)
+            .build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonAppFromOverview() {
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+            .addChange(change)
+            .build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
+            .addChange(change)
+            .build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitToFront_logTaskAddedAndEnterReasonOverview() {
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+    // previous exit to overview transition
+    val previousSessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(previousSessionId)
+    val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val previousTransitionInfo =
+        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+            .addChange(previousChange)
+            .build()
+
+    callOnTransitionReady(previousTransitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+
+    // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+    // next transition involving freeform windows
+
+    // TRANSIT_TO_FRONT
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+    // previous exit to overview transition
+    val previousSessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(previousSessionId)
+    val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val previousTransitionInfo =
+        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+            .addChange(previousChange)
+            .build()
+
+    callOnTransitionReady(previousTransitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+
+    // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+    // next transition involving freeform windows
+
+    // TRANSIT_CHANGE
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_CHANGE, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() {
+    // previous exit to overview transition
+    val previousSessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(previousSessionId)
+    val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val previousTransitionInfo =
+        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+            .addChange(previousChange)
+            .build()
+
+    callOnTransitionReady(previousTransitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+
+    // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
+    // next transition involving freeform windows
+
+    // TRANSIT_OPEN
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.OVERVIEW))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  @Suppress("ktlint:standard:max-line-length")
+  fun transitEnterDesktopFromAppFromOverview_previousTransitionExitToOverview_logTaskAddedAndEnterReasonAppFromOverview() {
+    // Tests for AppFromOverview precedence in compared to cancelled Overview
+
+    // previous exit to overview transition
+    val previousSessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(previousSessionId)
+    val previousChange = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val previousTransitionInfo =
+      TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
+              .addChange(previousChange)
+              .build()
+
+    callOnTransitionReady(previousTransitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(previousSessionId), any())
+    verify(desktopModeEventLogger, times(1))
+            .logSessionExit(eq(previousSessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+
+    // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo =
+      TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
+              .addChange(change)
+              .build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+            .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+    val sessionId = transitionObserver.getLoggerSessionId()
+
+    assertThat(sessionId).isNotNull()
+    verify(desktopModeEventLogger, times(1))
+        .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON))
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verifyZeroInteractions(desktopModeEventLogger)
+  }
+
+  @Test
+  fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() {
+    val sessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF))
+    verifyZeroInteractions(desktopModeEventLogger)
+    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+  }
+
+  @Test
+  fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
+    val sessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // window mode changing from FREEFORM to FULLSCREEN
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT))
+    verifyZeroInteractions(desktopModeEventLogger)
+    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+  }
+
+  @Test
+  fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
+    val sessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // window mode changing from FREEFORM to FULLSCREEN
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
+            .addChange(change)
+            .build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT))
+    verifyZeroInteractions(desktopModeEventLogger)
+    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+  }
+
+  @Test
+  fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
+    val sessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // window mode changing from FREEFORM to FULLSCREEN
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT))
+    verifyZeroInteractions(desktopModeEventLogger)
+    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+  }
+
+  @Test
+  fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
+    val sessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // window mode changing from FREEFORM to FULLSCREEN
+    val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT))
+    verifyZeroInteractions(desktopModeEventLogger)
+    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+  }
+
+  @Test
+  fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
+    val sessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // recents transition
+    val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo =
+        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+    verifyZeroInteractions(desktopModeEventLogger)
+    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+  }
+
+  @Test
+  fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
+    val sessionId = 1
+    // add a freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // task closing
+    val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED))
+    verifyZeroInteractions(desktopModeEventLogger)
+    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+  }
+
+  @Test
+  fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
+    val sessionId = 1
+    // add a freeform task to an existing session
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // recents transition sent freeform window to back
+    val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    val transitionInfo1 =
+        TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build()
+    callOnTransitionReady(transitionInfo1)
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, times(1))
+        .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
+    assertThat(transitionObserver.getLoggerSessionId()).isNull()
+
+    val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
+    callOnTransitionReady(transitionInfo2)
+
+    verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any())
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any())
+  }
+
+  @Test
+  fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
+    val sessionId = 1
+    // add an existing freeform task
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // new freeform task added
+    val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
+    verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
+  }
+
+  @Test
+  fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
+    val sessionId = 1
+    // add two existing freeform tasks
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
+    transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+    transitionObserver.setLoggerSessionId(sessionId)
+
+    // new freeform task added
+    val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
+    callOnTransitionReady(transitionInfo)
+
+    verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
+    verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
+  }
+
+  /** Simulate calling the onTransitionReady() method */
+  private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
+    val transition = mock(IBinder::class.java)
+    val startT = mock(SurfaceControl.Transaction::class.java)
+    val finishT = mock(SurfaceControl.Transaction::class.java)
+
+    transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
+  }
+
+  companion object {
+    fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo {
+      val taskInfo = ActivityManager.RunningTaskInfo()
+      taskInfo.taskId = taskId
+      taskInfo.configuration.windowConfiguration.windowingMode = windowMode
+
+      return taskInfo
     }
 
-    @Test
-    fun testRegistersObserverAtInit() {
-        verify(transitions).registerObserver(same(transitionObserver))
+    fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
+      val change =
+          Change(
+              WindowContainerToken(mock(IWindowContainerToken::class.java)),
+              mock(SurfaceControl::class.java))
+      change.mode = mode
+      change.taskInfo = taskInfo
+      return change
     }
-
-    @Test
-    fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() {
-        val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
-        val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
-        verify(desktopModeEventLogger, never()).logTaskAdded(any(), any())
-    }
-
-    @Test
-    fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() {
-        val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-
-        callOnTransitionReady(transitionInfo)
-        val sessionId = transitionObserver.getLoggerSessionId()
-
-        assertThat(sessionId).isNotNull()
-        verify(desktopModeEventLogger, times(1))
-            .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT))
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-        verifyZeroInteractions(desktopModeEventLogger)
-    }
-
-    @Test
-    fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() {
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        // task change is finalised when drag ends
-        val transitionInfo =
-            TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
-                .addChange(change)
-                .build()
-
-        callOnTransitionReady(transitionInfo)
-        val sessionId = transitionObserver.getLoggerSessionId()
-
-        assertThat(sessionId).isNotNull()
-        verify(desktopModeEventLogger, times(1))
-            .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG))
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-        verifyZeroInteractions(desktopModeEventLogger)
-    }
-
-    @Test
-    fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() {
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0)
-                .addChange(change)
-                .build()
-
-        callOnTransitionReady(transitionInfo)
-        val sessionId = transitionObserver.getLoggerSessionId()
-
-        assertThat(sessionId).isNotNull()
-        verify(desktopModeEventLogger, times(1))
-            .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON))
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-        verifyZeroInteractions(desktopModeEventLogger)
-    }
-
-    @Test
-    // TODO(b/344822506): Update test when we add enter reason for app from overview
-    fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() {
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0)
-                .addChange(change)
-                .build()
-
-        callOnTransitionReady(transitionInfo)
-        val sessionId = transitionObserver.getLoggerSessionId()
-
-        assertThat(sessionId).isNotNull()
-        verify(desktopModeEventLogger, times(1))
-            .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-        verifyZeroInteractions(desktopModeEventLogger)
-    }
-
-    @Test
-    fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() {
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0)
-                .addChange(change)
-                .build()
-
-        callOnTransitionReady(transitionInfo)
-        val sessionId = transitionObserver.getLoggerSessionId()
-
-        assertThat(sessionId).isNotNull()
-        verify(desktopModeEventLogger, times(1))
-            .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER))
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-        verifyZeroInteractions(desktopModeEventLogger)
-    }
-
-    @Test
-    fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() {
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build()
-
-        callOnTransitionReady(transitionInfo)
-        val sessionId = transitionObserver.getLoggerSessionId()
-
-        assertThat(sessionId).isNotNull()
-        verify(desktopModeEventLogger, times(1))
-            .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER))
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-        verifyZeroInteractions(desktopModeEventLogger)
-    }
-
-    @Test
-    fun transitWake_logTaskAddedAndEnterReasonScreenOn() {
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build()
-
-        callOnTransitionReady(transitionInfo)
-        val sessionId = transitionObserver.getLoggerSessionId()
-
-        assertThat(sessionId).isNotNull()
-        verify(desktopModeEventLogger, times(1))
-            .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON))
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-        verifyZeroInteractions(desktopModeEventLogger)
-    }
-
-    @Test
-    fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() {
-        val sessionId = 1
-        // add a freeform task
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF))
-        verifyZeroInteractions(desktopModeEventLogger)
-        assertThat(transitionObserver.getLoggerSessionId()).isNull()
-    }
-
-    @Test
-    fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() {
-        val sessionId = 1
-        // add a freeform task
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // window mode changing from FREEFORM to FULLSCREEN
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT))
-        verifyZeroInteractions(desktopModeEventLogger)
-        assertThat(transitionObserver.getLoggerSessionId()).isNull()
-    }
-
-    @Test
-    fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() {
-        val sessionId = 1
-        // add a freeform task
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // window mode changing from FREEFORM to FULLSCREEN
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON)
-                .addChange(change)
-                .build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT))
-        verifyZeroInteractions(desktopModeEventLogger)
-        assertThat(transitionObserver.getLoggerSessionId()).isNull()
-    }
-
-    @Test
-    fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() {
-        val sessionId = 1
-        // add a freeform task
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // window mode changing from FREEFORM to FULLSCREEN
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT)
-                .addChange(change)
-                .build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT))
-        verifyZeroInteractions(desktopModeEventLogger)
-        assertThat(transitionObserver.getLoggerSessionId()).isNull()
-    }
-
-    @Test
-    fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() {
-        val sessionId = 1
-        // add a freeform task
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // window mode changing from FREEFORM to FULLSCREEN
-        val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT))
-        verifyZeroInteractions(desktopModeEventLogger)
-        assertThat(transitionObserver.getLoggerSessionId()).isNull()
-    }
-
-    @Test
-    fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() {
-        val sessionId = 1
-        // add a freeform task
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // recents transition
-        val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        val transitionInfo =
-            TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-                .addChange(change)
-                .build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
-        verifyZeroInteractions(desktopModeEventLogger)
-        assertThat(transitionObserver.getLoggerSessionId()).isNull()
-    }
-
-    @Test
-    fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() {
-        val sessionId = 1
-        // add a freeform task
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // task closing
-        val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN))
-        val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED))
-        verifyZeroInteractions(desktopModeEventLogger)
-        assertThat(transitionObserver.getLoggerSessionId()).isNull()
-    }
-
-    @Test
-    fun sessionExitByRecents_cancelledAnimation_sessionRestored() {
-        val sessionId = 1
-        // add a freeform task to an existing session
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // recents transition sent freeform window to back
-        val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        val transitionInfo1 =
-            TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS)
-                .addChange(change)
-                .build()
-        callOnTransitionReady(transitionInfo1)
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, times(1))
-            .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW))
-        assertThat(transitionObserver.getLoggerSessionId()).isNull()
-
-        val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
-        callOnTransitionReady(transitionInfo2)
-
-        verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any())
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any())
-    }
-
-    @Test
-    fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() {
-        val sessionId = 1
-        // add an existing freeform task
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // new freeform task added
-        val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
-        val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any())
-        verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
-    }
-
-    @Test
-    fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() {
-        val sessionId = 1
-        // add two existing freeform tasks
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM))
-        transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM))
-        transitionObserver.setLoggerSessionId(sessionId)
-
-        // new freeform task added
-        val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM))
-        val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build()
-        callOnTransitionReady(transitionInfo)
-
-        verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any())
-        verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
-    }
-
-    /** Simulate calling the onTransitionReady() method */
-    private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
-        val transition = mock(IBinder::class.java)
-        val startT = mock(SurfaceControl.Transaction::class.java)
-        val finishT = mock(SurfaceControl.Transaction::class.java)
-
-        transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT)
-    }
-
-    companion object {
-        fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo {
-            val taskInfo = ActivityManager.RunningTaskInfo()
-            taskInfo.taskId = taskId
-            taskInfo.configuration.windowConfiguration.windowingMode = windowMode
-
-            return taskInfo
-        }
-
-        fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change {
-            val change =
-                Change(
-                    WindowContainerToken(mock(IWindowContainerToken::class.java)),
-                    mock(SurfaceControl::class.java)
-                )
-            change.mode = mode
-            change.taskInfo = taskInfo
-            return change
-        }
-    }
+  }
 }
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 b526838..e17f7f2 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
@@ -24,6 +24,7 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.ComponentName
 import android.content.Intent
 import android.content.pm.ActivityInfo
 import android.content.pm.ActivityInfo.CONFIG_DENSITY
@@ -484,57 +485,52 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task = setUpFullscreenTask()
     setUpLandscapeDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
     setUpLandscapeDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task =
         setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
     setUpLandscapeDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task =
         setUpFullscreenTask(isResizable = false, screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
     setUpLandscapeDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task =
         setUpFullscreenTask(
             isResizable = false,
@@ -543,26 +539,24 @@
     setUpLandscapeDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
     setUpPortraitDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task =
         setUpFullscreenTask(
             deviceOrientation = ORIENTATION_PORTRAIT,
@@ -570,14 +564,13 @@
     setUpPortraitDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task =
         setUpFullscreenTask(
             deviceOrientation = ORIENTATION_PORTRAIT,
@@ -586,14 +579,13 @@
     setUpPortraitDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task =
         setUpFullscreenTask(
             isResizable = false,
@@ -602,14 +594,13 @@
     setUpPortraitDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
   }
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun moveToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val task =
         setUpFullscreenTask(
             isResizable = false,
@@ -619,7 +610,7 @@
     setUpPortraitDisplay()
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
   }
 
@@ -629,7 +620,7 @@
     val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
     tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
   }
 
@@ -639,7 +630,7 @@
     val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
     tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
         .isEqualTo(WINDOWING_MODE_UNDEFINED)
   }
@@ -647,7 +638,7 @@
   @Test
   fun moveToDesktop_nonExistentTask_doesNothing() {
     controller.moveToDesktop(999, transitionSource = UNKNOWN)
-    verifyWCTNotExecuted()
+    verifyEnterDesktopWCTNotExecuted()
   }
 
   @Test
@@ -659,7 +650,7 @@
     whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
 
     controller.moveToDesktop(task.taskId, transitionSource = UNKNOWN)
-    with(getLatestMoveToDesktopWct()) {
+    with(getLatestEnterDesktopWct()) {
       assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
     }
   }
@@ -674,34 +665,21 @@
         }
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    verifyWCTNotExecuted()
+    verifyEnterDesktopWCTNotExecuted()
   }
 
   @Test
-  fun moveToDesktop_deviceNotSupported_doesNothing() {
+  fun moveToDesktop_systemUIActivity_doesNothing() {
     val task = setUpFullscreenTask()
 
-    // Simulate non compatible device
-    doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+    // Set task as systemUI package
+    val systemUIPackageName = context.resources.getString(
+      com.android.internal.R.string.config_systemUi)
+    val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+    task.baseActivity = baseComponent
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    verifyWCTNotExecuted()
-  }
-
-  @Test
-  fun moveToDesktop_deviceNotSupported_deviceRestrictionsOverridden_taskIsMovedToDesktop() {
-    val task = setUpFullscreenTask()
-
-    // Simulate non compatible device
-    doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
-
-    // Simulate enforce device restrictions system property overridden to false
-    whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(false)
-
-    controller.moveToDesktop(task, transitionSource = UNKNOWN)
-
-    val wct = getLatestMoveToDesktopWct()
-    assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
+    verifyEnterDesktopWCTNotExecuted()
   }
 
   @Test
@@ -710,7 +688,7 @@
 
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
 
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
   }
 
@@ -724,7 +702,7 @@
 
     controller.moveToDesktop(fullscreenTask, transitionSource = UNKNOWN)
 
-    with(getLatestMoveToDesktopWct()) {
+    with(getLatestEnterDesktopWct()) {
       // Operations should include home task, freeform task
       assertThat(hierarchyOps).hasSize(3)
       assertReorderSequence(homeTask, freeformTask, fullscreenTask)
@@ -742,7 +720,7 @@
 
     controller.moveToDesktop(fullscreenTask, transitionSource = UNKNOWN)
 
-    with(getLatestMoveToDesktopWct()) {
+    with(getLatestEnterDesktopWct()) {
       // Operations should include wallpaper intent, freeform task, fullscreen task
       assertThat(hierarchyOps).hasSize(3)
       assertPendingIntentAt(index = 0, desktopWallpaperIntent)
@@ -766,7 +744,7 @@
 
     controller.moveToDesktop(fullscreenTaskDefault, transitionSource = UNKNOWN)
 
-    with(getLatestMoveToDesktopWct()) {
+    with(getLatestEnterDesktopWct()) {
       // Check that hierarchy operations do not include tasks from second display
       assertThat(hierarchyOps.map { it.container }).doesNotContain(homeTaskSecond.token.asBinder())
       assertThat(hierarchyOps.map { it.container })
@@ -778,7 +756,7 @@
   fun moveToDesktop_splitTaskExitsSplit() {
     val task = setUpSplitScreenTask()
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
     verify(splitScreenController)
         .prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
@@ -788,7 +766,7 @@
   fun moveToDesktop_fullscreenTaskDoesNotExitSplit() {
     val task = setUpFullscreenTask()
     controller.moveToDesktop(task, transitionSource = UNKNOWN)
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task.token.asBinder()]?.windowingMode).isEqualTo(WINDOWING_MODE_FREEFORM)
     verify(splitScreenController, never())
         .prepareExitSplitScreen(any(), anyInt(), eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE))
@@ -803,7 +781,7 @@
 
     controller.moveToDesktop(newTask, transitionSource = UNKNOWN)
 
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(wct.hierarchyOps.size).isEqualTo(taskLimit + 1) // visible tasks + home
     wct.assertReorderAt(0, homeTask)
     for (i in 1..<taskLimit) { // Skipping freeformTasks[0]
@@ -837,7 +815,7 @@
   @Test
   fun moveToFullscreen_nonExistentTask_doesNothing() {
     controller.moveToFullscreen(999, transitionSource = UNKNOWN)
-    verifyWCTNotExecuted()
+    verifyExitDesktopWCTNotExecuted()
   }
 
   @Test
@@ -1113,10 +1091,8 @@
 
   @Test
   fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() {
-    // TODO(344599474) enable the test once the density change is behind a flag
-    assumeTrue(false)
     assumeTrue(ENABLE_SHELL_TRANSITIONS)
-    whenever(DesktopModeStatus.isDesktopDensityOverrideSet()).thenReturn(false)
+    whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false)
 
     val freeformTask1 = setUpFreeformTask()
     markTaskVisible(freeformTask1)
@@ -1129,10 +1105,8 @@
 
   @Test
   fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() {
-    // TODO(344599474) enable the test once the density change is behind a flag
-    assumeTrue(false)
     assumeTrue(ENABLE_SHELL_TRANSITIONS)
-    whenever(DesktopModeStatus.isDesktopDensityOverrideSet()).thenReturn(true)
+    whenever(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true)
 
     val freeformTask1 = setUpFreeformTask()
     markTaskVisible(freeformTask1)
@@ -1211,6 +1185,21 @@
   }
 
   @Test
+  fun handleRequest_systemUIActivity_returnSwitchToFullscreenWCT() {
+    val task = setUpFreeformTask()
+
+    // Set task as systemUI package
+    val systemUIPackageName = context.resources.getString(
+      com.android.internal.R.string.config_systemUi)
+    val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+    task.baseActivity = baseComponent
+
+    val result = controller.handleRequest(Binder(), createTransition(task))
+    assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+  }
+
+  @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
   fun handleRequest_backTransition_singleActiveTask_noToken() {
     val task = setUpFreeformTask()
@@ -1288,7 +1277,7 @@
 
     controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
 
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task1.token.asBinder()]?.windowingMode)
         .isEqualTo(WINDOWING_MODE_FREEFORM)
   }
@@ -1309,7 +1298,7 @@
 
     controller.moveFocusedTaskToDesktop(DEFAULT_DISPLAY, transitionSource = UNKNOWN)
 
-    val wct = getLatestMoveToDesktopWct()
+    val wct = getLatestEnterDesktopWct()
     assertThat(wct.changes[task4.token.asBinder()]?.windowingMode)
         .isEqualTo(WINDOWING_MODE_FREEFORM)
     verify(splitScreenController)
@@ -1336,7 +1325,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1353,7 +1341,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1370,7 +1357,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1388,7 +1374,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1406,7 +1391,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1427,7 +1411,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1444,7 +1427,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1464,7 +1446,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1485,7 +1466,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1506,7 +1486,6 @@
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
-    doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
     whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
@@ -1632,6 +1611,8 @@
       bounds: Rect? = null
   ): RunningTaskInfo {
     val task = createFreeformTask(displayId, bounds)
+    val activityInfo = ActivityInfo()
+    task.topActivityInfo = activityInfo
     whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
     desktopModeTaskRepository.addActiveTask(displayId, task.taskId)
     desktopModeTaskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
@@ -1689,7 +1670,6 @@
             Rect(0, 0, DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
       }
     }
-    whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
     whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
     runningTasks.add(task)
     return task
@@ -1707,7 +1687,6 @@
 
   private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
     val task = createSplitScreenTask(displayId)
-    whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
     whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)
     whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
     runningTasks.add(task)
@@ -1752,7 +1731,7 @@
     return arg.value
   }
 
-  private fun getLatestMoveToDesktopWct(): WindowContainerTransaction {
+  private fun getLatestEnterDesktopWct(): WindowContainerTransaction {
     val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
     if (ENABLE_SHELL_TRANSITIONS) {
       verify(enterDesktopTransitionHandler).moveToDesktop(arg.capture(), any())
@@ -1794,6 +1773,22 @@
     }
   }
 
+  private fun verifyExitDesktopWCTNotExecuted() {
+    if (ENABLE_SHELL_TRANSITIONS) {
+      verify(exitDesktopTransitionHandler, never()).startTransition(any(), any(), any(), any())
+    } else {
+      verify(shellTaskOrganizer, never()).applyTransaction(any())
+    }
+  }
+
+  private fun verifyEnterDesktopWCTNotExecuted() {
+    if (ENABLE_SHELL_TRANSITIONS) {
+      verify(enterDesktopTransitionHandler, never()).moveToDesktop(any(), any())
+    } else {
+      verify(shellTaskOrganizer, never()).applyTransaction(any())
+    }
+  }
+
   private fun createTransition(
       task: RunningTaskInfo?,
       @WindowManager.TransitionType type: Int = TRANSIT_OPEN
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 56c4cea..e291c0e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -113,6 +113,8 @@
     private DisplayInsetsController mDisplayInsetsController;
     @Mock
     private IRecentTasksListener mRecentTasksListener;
+    @Mock
+    private TaskStackTransitionObserver mTaskStackTransitionObserver;
 
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -139,7 +141,8 @@
                 mDisplayInsetsController, mMainExecutor));
         mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
                 mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
-                Optional.of(mDesktopModeTaskRepository), mMainExecutor);
+                Optional.of(mDesktopModeTaskRepository), mTaskStackTransitionObserver,
+                mMainExecutor);
         mRecentTasksController = spy(mRecentTasksControllerReal);
         mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
                 null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
@@ -557,6 +560,30 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    public void onTaskMovedToFront_TaskStackObserverEnabled_triggersOnTaskMovedToFront()
+            throws Exception {
+        mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+        ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+        mRecentTasksControllerReal.onTaskMovedToFrontThroughTransition(taskInfo);
+
+        verify(mRecentTasksListener).onTaskMovedToFront(taskInfo);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    public void onTaskMovedToFront_TaskStackObserverEnabled_doesNotTriggersOnTaskMovedToFront()
+            throws Exception {
+        mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener);
+        ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10);
+
+        mRecentTasksControllerReal.onTaskMovedToFront(taskInfo);
+
+        verify(mRecentTasksListener, never()).onTaskMovedToFront(any());
+    }
+
+    @Test
     public void getNullSplitBoundsNonSplitTask() {
         SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3);
         assertNull("splitBounds should be null for non-split task", sb);
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
new file mode 100644
index 0000000..f959970
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -0,0 +1,217 @@
+/*
+ * 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.recents
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.os.IBinder
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import android.view.SurfaceControl
+import android.view.WindowManager
+import android.window.IWindowContainerToken
+import android.window.TransitionInfo
+import android.window.WindowContainerToken
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.TransitionInfoBuilder
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.same
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+
+/**
+ * Test class for {@link TaskStackTransitionObserver}
+ *
+ * Usage: atest WMShellUnitTests:TaskStackTransitionObserverTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TaskStackTransitionObserverTest {
+
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+    @Mock private lateinit var shellInit: ShellInit
+    @Mock lateinit var testExecutor: ShellExecutor
+    @Mock private lateinit var transitionsLazy: Lazy<Transitions>
+    @Mock private lateinit var transitions: Transitions
+    @Mock private lateinit var mockTransitionBinder: IBinder
+
+    private lateinit var transitionObserver: TaskStackTransitionObserver
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        shellInit = Mockito.spy(ShellInit(testExecutor))
+        whenever(transitionsLazy.get()).thenReturn(transitions)
+        transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit)
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java)
+            verify(shellInit)
+                .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver))
+            initRunnableCaptor.value.run()
+        } else {
+            transitionObserver.onInit()
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    fun testRegistersObserverAtInit() {
+        verify(transitions).registerObserver(same(transitionObserver))
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    fun taskCreated_freeformWindow_listenerNotified() {
+        val listener = TestListener()
+        val executor = TestShellExecutor()
+        transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+        val change =
+            createChange(
+                WindowManager.TRANSIT_OPEN,
+                createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+            )
+        val transitionInfo =
+            TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+        callOnTransitionFinished()
+        executor.flushAll()
+
+        assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId)
+        assertThat(listener.taskInfoToBeNotified.windowingMode)
+            .isEqualTo(change.taskInfo?.windowingMode)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    fun taskCreated_fullscreenWindow_listenerNotNotified() {
+        val listener = TestListener()
+        val executor = TestShellExecutor()
+        transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+        val change =
+            createChange(
+                WindowManager.TRANSIT_OPEN,
+                createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+            )
+        val transitionInfo =
+            TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build()
+
+        callOnTransitionReady(transitionInfo)
+        callOnTransitionFinished()
+        executor.flushAll()
+
+        assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(0)
+        assertThat(listener.taskInfoToBeNotified.windowingMode)
+            .isEqualTo(WindowConfiguration.WINDOWING_MODE_UNDEFINED)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL)
+    fun taskCreated_freeformWindowOnTopOfFreeform_listenerNotified() {
+        val listener = TestListener()
+        val executor = TestShellExecutor()
+        transitionObserver.addTaskStackTransitionObserverListener(listener, executor)
+        val freeformOpenChange =
+            createChange(
+                WindowManager.TRANSIT_OPEN,
+                createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+            )
+        val freeformReorderChange =
+            createChange(
+                WindowManager.TRANSIT_TO_BACK,
+                createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM)
+            )
+        val transitionInfo =
+            TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0)
+                .addChange(freeformOpenChange)
+                .addChange(freeformReorderChange)
+                .build()
+
+        callOnTransitionReady(transitionInfo)
+        callOnTransitionFinished()
+        executor.flushAll()
+
+        assertThat(listener.taskInfoToBeNotified.taskId)
+            .isEqualTo(freeformOpenChange.taskInfo?.taskId)
+        assertThat(listener.taskInfoToBeNotified.windowingMode)
+            .isEqualTo(freeformOpenChange.taskInfo?.windowingMode)
+    }
+
+    class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener {
+        var taskInfoToBeNotified = ActivityManager.RunningTaskInfo()
+
+        override fun onTaskMovedToFrontThroughTransition(
+            taskInfo: ActivityManager.RunningTaskInfo
+        ) {
+            taskInfoToBeNotified = taskInfo
+        }
+    }
+
+    /** Simulate calling the onTransitionReady() method */
+    private fun callOnTransitionReady(transitionInfo: TransitionInfo) {
+        val startT = Mockito.mock(SurfaceControl.Transaction::class.java)
+        val finishT = Mockito.mock(SurfaceControl.Transaction::class.java)
+
+        transitionObserver.onTransitionReady(mockTransitionBinder, transitionInfo, startT, finishT)
+    }
+
+    /** Simulate calling the onTransitionFinished() method */
+    private fun callOnTransitionFinished() {
+        transitionObserver.onTransitionFinished(mockTransitionBinder, false)
+    }
+
+    companion object {
+        fun createTaskInfo(taskId: Int, windowingMode: Int): ActivityManager.RunningTaskInfo {
+            val taskInfo = ActivityManager.RunningTaskInfo()
+            taskInfo.taskId = taskId
+            taskInfo.configuration.windowConfiguration.windowingMode = windowingMode
+
+            return taskInfo
+        }
+
+        fun createChange(
+            mode: Int,
+            taskInfo: ActivityManager.RunningTaskInfo
+        ): TransitionInfo.Change {
+            val change =
+                TransitionInfo.Change(
+                    WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java)),
+                    Mockito.mock(SurfaceControl::class.java)
+                )
+            change.mode = mode
+            change.taskInfo = taskInfo
+            return change
+        }
+    }
+}
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 9c1dc22..ca1e3f1 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
@@ -22,7 +22,9 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.ComponentName
 import android.content.Context
+import android.content.pm.ActivityInfo
 import android.graphics.Rect
 import android.hardware.display.DisplayManager
 import android.hardware.display.VirtualDisplay
@@ -341,7 +343,7 @@
     }
 
     @Test
-    fun testDescorationIsNotCreatedForTopTranslucentActivities() {
+    fun testDecorationIsNotCreatedForTopTranslucentActivities() {
         setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
         val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
             isTopActivityTransparent = true
@@ -354,6 +356,22 @@
     }
 
     @Test
+    fun testDecorationIsNotCreatedForSystemUIActivities() {
+        val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
+
+        // Set task as systemUI package
+        val systemUIPackageName = context.resources.getString(
+            com.android.internal.R.string.config_systemUi)
+        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        task.baseActivity = baseComponent
+
+        onTaskOpening(task)
+
+        verify(mockDesktopModeWindowDecorFactory, never())
+                .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+    }
+
+    @Test
     @RequiresFlagsEnabled(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_IMMERSIVE_HANDLE_HIDING)
     fun testRelayoutRunsWhenStatusBarsInsetsSourceVisibilityChanges() {
         val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM, focused = true)
@@ -522,7 +540,8 @@
             displayId: Int = DEFAULT_DISPLAY,
             @WindowConfiguration.WindowingMode windowingMode: Int,
             activityType: Int = ACTIVITY_TYPE_STANDARD,
-            focused: Boolean = true
+            focused: Boolean = true,
+            activityInfo: ActivityInfo = ActivityInfo()
     ): RunningTaskInfo {
         return TestRunningTaskInfoBuilder()
                 .setDisplayId(displayId)
@@ -530,6 +549,7 @@
                 .setVisible(true)
                 .setActivityType(activityType)
                 .build().apply {
+                    topActivityInfo = activityInfo
                     isFocused = focused
                 }
     }
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 a731e53..46c1589 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
@@ -22,11 +22,16 @@
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -35,7 +40,7 @@
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Handler;
@@ -45,15 +50,22 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableContext;
+import android.view.AttachedSurfaceControl;
 import android.view.Choreographer;
 import android.view.Display;
+import android.view.GestureDetector;
+import android.view.InsetsState;
+import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
+import android.view.View;
 import android.view.WindowManager;
 import android.window.WindowContainerTransaction;
 
+import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.internal.R;
 import com.android.window.flags.Flags;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -62,14 +74,18 @@
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.shared.DesktopModeStatus;
 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.quality.Strictness;
 
 import java.util.function.Supplier;
 
@@ -106,18 +122,26 @@
     @Mock
     private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier;
     @Mock
-    private SurfaceControl.Transaction mMockTransaction;
-    @Mock
     private SurfaceControl mMockSurfaceControl;
     @Mock
     private SurfaceControlViewHost mMockSurfaceControlViewHost;
     @Mock
+    private AttachedSurfaceControl mMockRootSurfaceControl;
+    @Mock
     private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
     @Mock
     private TypedArray mMockRoundedCornersRadiusArray;
 
-    private final Configuration mConfiguration = new Configuration();
+    @Mock
+    private TestTouchEventListener mMockTouchEventListener;
+    @Mock
+    private DesktopModeWindowDecoration.ExclusionRegionListener mMockExclusionRegionListener;
+    @Mock
+    private PackageManager mMockPackageManager;
 
+    private final InsetsState mInsetsState = new InsetsState();
+    private SurfaceControl.Transaction mMockTransaction;
+    private StaticMockitoSession mMockitoSession;
     private TestableContext mTestableContext;
 
     /** Set up run before test class. */
@@ -131,11 +155,29 @@
 
     @Before
     public void setUp() {
+        mMockitoSession = mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DesktopModeStatus.class)
+                .startMocking();
+        when(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false);
         doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create(
                 any(), any(), any());
+        when(mMockSurfaceControlViewHost.getRootSurfaceControl())
+                .thenReturn(mMockRootSurfaceControl);
+        mMockTransaction = createMockSurfaceControlTransaction();
         doReturn(mMockTransaction).when(mMockTransactionSupplier).get();
         mTestableContext = new TestableContext(mContext);
         mTestableContext.ensureTestableResources();
+        mContext.setMockPackageManager(mMockPackageManager);
+        when(mMockPackageManager.getApplicationLabel(any())).thenReturn("applicationLabel");
+        final Display defaultDisplay = mock(Display.class);
+        doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
+        doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
+    }
+
+    @After
+    public void tearDown() {
+        mMockitoSession.finishMocking();
     }
 
     @Test
@@ -206,6 +248,7 @@
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
     public void updateRelayoutParams_appHeader_usesSystemDensity() {
+        when(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(true);
         final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
                 .getConfiguration().densityDpi;
         final int customTaskDensity = systemDensity + 300;
@@ -323,6 +366,99 @@
         assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue();
     }
 
+    @Test
+    public void relayout_fullscreenTask_appliesTransactionImmediately() {
+        final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+        final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        spyWindowDecor.relayout(taskInfo);
+
+        verify(mMockTransaction).apply();
+        verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any());
+    }
+
+    @Test
+    public void relayout_freeformTask_appliesTransactionOnDraw() {
+        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(mMockTransaction, never()).apply();
+        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);
+
+        verify(mMockHandler).post(runnableArgument.capture());
+        runnableArgument.getValue().run();
+        verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
+    }
+
+    @Test
+    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);
+        verify(mMockHandler).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);
+        verify(mMockHandler).post(runnableArgument.capture());
+
+        spyWindowDecor.close();
+
+        verify(mMockHandler).removeCallbacks(runnableArgument.getValue());
+    }
+
     private void fillRoundedCornersResources(int fillValue) {
         when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt()))
                 .thenReturn(fillValue);
@@ -343,12 +479,16 @@
 
     private DesktopModeWindowDecoration createWindowDecoration(
             ActivityManager.RunningTaskInfo taskInfo) {
-        return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
-                mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl,
+        DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext,
+                mMockDisplayController, mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl,
                 mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
                 SurfaceControl.Builder::new, mMockTransactionSupplier,
                 WindowContainerTransaction::new, SurfaceControl::new,
                 mMockSurfaceControlViewHostFactory);
+        windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
+                mMockTouchEventListener, mMockTouchEventListener);
+        windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
+        return windowDecor;
     }
 
     private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) {
@@ -373,4 +513,32 @@
         return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL)
                 != 0;
     }
+
+    private static class TestTouchEventListener extends GestureDetector.SimpleOnGestureListener
+            implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
+            View.OnGenericMotionListener, DragDetector.MotionEventHandler {
+
+        @Override
+        public void onClick(View v) {}
+
+        @Override
+        public boolean onGenericMotion(View v, MotionEvent event) {
+            return false;
+        }
+
+        @Override
+        public boolean onLongClick(View v) {
+            return false;
+        }
+
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            return false;
+        }
+
+        @Override
+        public boolean handleMotionEvent(@Nullable View v, MotionEvent ev) {
+            return false;
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index e6fabcf..f750e6b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -16,14 +16,20 @@
 package com.android.wm.shell.windowdecor
 
 import android.app.ActivityManager
+import android.content.Context
+import android.content.res.Resources
 import android.graphics.PointF
 import android.graphics.Rect
 import android.os.IBinder
+import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.view.Display
 import android.window.WindowContainerToken
+import com.android.window.flags.Flags
+import com.android.wm.shell.R
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_BOTTOM
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_RIGHT
 import com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_TOP
@@ -33,8 +39,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.Mockito.any
+import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
 /**
@@ -57,6 +63,10 @@
     private lateinit var mockDisplayLayout: DisplayLayout
     @Mock
     private lateinit var mockDisplay: Display
+    @Mock
+    private lateinit var mockContext: Context
+    @Mock
+    private lateinit var mockResources: Resources
 
     @Before
     fun setup() {
@@ -69,16 +79,15 @@
             (i.arguments.first() as Rect).set(STABLE_BOUNDS)
         }
 
-        mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
-            taskId = TASK_ID
-            token = taskToken
-            minWidth = MIN_WIDTH
-            minHeight = MIN_HEIGHT
-            defaultMinSize = DEFAULT_MIN
-            displayId = DISPLAY_ID
-            configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
-        }
+        initializeTaskInfo()
         mockWindowDecoration.mDisplay = mockDisplay
+        mockWindowDecoration.mDecorWindowContext = mockContext
+        whenever(mockContext.getResources()).thenReturn(mockResources)
+        whenever(mockWindowDecoration.mDecorWindowContext.resources).thenReturn(mockResources)
+        whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_width))
+                .thenReturn(DESKTOP_MODE_MIN_WIDTH)
+        whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_height))
+                .thenReturn(DESKTOP_MODE_MIN_HEIGHT)
         whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
     }
 
@@ -93,8 +102,8 @@
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
         DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
-                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
-                mockDisplayController, mockWindowDecoration)
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
 
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
@@ -113,8 +122,8 @@
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
         DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
-                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
-                mockDisplayController, mockWindowDecoration)
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
 
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 5)
@@ -127,14 +136,14 @@
         val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
         val repositionTaskBounds = Rect(STARTING_BOUNDS)
 
-        // Resize to width of 95px and width of -5px with minimum of 10px
+        // Resize to width of 95px and height of -5px with minimum of 10px
         val newX = STARTING_BOUNDS.right.toFloat() - 5
         val newY = STARTING_BOUNDS.top.toFloat() + 105
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
         DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
-                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
-                mockDisplayController, mockWindowDecoration)
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
 
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
@@ -153,8 +162,8 @@
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
         DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
-                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
-                mockDisplayController, mockWindowDecoration)
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 80)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80)
@@ -172,8 +181,8 @@
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
         DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
-                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
-            mockDisplayController, mockWindowDecoration)
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
@@ -196,14 +205,13 @@
         assertThat(repositionTaskBounds.left).isEqualTo(validDragArea.left)
         assertThat(repositionTaskBounds.top).isEqualTo(validDragArea.bottom)
         assertThat(repositionTaskBounds.right)
-            .isEqualTo(validDragArea.left + STARTING_BOUNDS.width())
+                .isEqualTo(validDragArea.left + STARTING_BOUNDS.width())
         assertThat(repositionTaskBounds.bottom)
-            .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height())
+                .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height())
     }
 
     @Test
     fun testChangeBounds_toDisallowedBounds_freezesAtLimit() {
-        var hasMoved = false
         val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(),
             STARTING_BOUNDS.bottom.toFloat())
         val repositionTaskBounds = Rect(STARTING_BOUNDS)
@@ -212,26 +220,127 @@
         var newY = STARTING_BOUNDS.bottom.toFloat() + 10
         var delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
         assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
-                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
-                mockDisplayController, mockWindowDecoration))
-        hasMoved = true
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration))
         // Resize width to 120px, height to disallowed area which should not result in a change.
         newX += 10
         newY = DISALLOWED_RESIZE_AREA.top.toFloat()
         delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
         assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
-            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta,
-            mockDisplayController, mockWindowDecoration))
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration))
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right + 20)
         assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom + 10)
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
+    fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeLessThanMin_shouldNotChangeBounds() {
+        whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
+        initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
+        val startingPoint =
+            PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+        // Shrink height and width to 1px. The default allowed width and height are defined in
+        // R.dimen.desktop_mode_minimum_window_width and R.dimen.desktop_mode_minimum_window_height
+        val newX = STARTING_BOUNDS.right.toFloat() - 99
+        val newY = STARTING_BOUNDS.bottom.toFloat() - 99
+        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
+        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
+    fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() {
+        whenever(DesktopModeStatus.canEnterDesktopMode(mockContext)).thenReturn(true)
+        initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
+        val startingPoint =
+            PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+        // Shrink height and width to 20px. The default allowed width and height are defined in
+        // R.dimen.desktop_mode_minimum_window_width and R.dimen.desktop_mode_minimum_window_height
+        val newX = STARTING_BOUNDS.right.toFloat() - 80
+        val newY = STARTING_BOUNDS.bottom.toFloat() - 80
+        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
+        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom - 80)
+    }
+
+    @Test
+    fun taskMinWidthHeightUndefined_changeBoundsLessThanDefaultMinSize_shouldNotChangeBounds() {
+        initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
+        val startingPoint =
+            PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+        // Shrink height and width to 1px. The default allowed width and height are defined in the
+        // defaultMinSize of the TaskInfo.
+        val newX = STARTING_BOUNDS.right.toFloat() - 99
+        val newY = STARTING_BOUNDS.bottom.toFloat() - 99
+        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
+        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom)
+    }
+
+    @Test
+    fun taskMinWidthHeightUndefined_changeBoundsToAnAllowedSize_shouldChangeBounds() {
+        initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
+        val startingPoint =
+            PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+        // Shrink height and width to 50px. The default allowed width and height are defined in the
+        // defaultMinSize of the TaskInfo.
+        val newX = STARTING_BOUNDS.right.toFloat() - 50
+        val newY = STARTING_BOUNDS.bottom.toFloat() - 50
+        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+
+        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+            mockWindowDecoration)
+        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+        assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 50)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom - 50)
+    }
+
+    private fun initializeTaskInfo(taskMinWidth: Int = MIN_WIDTH, taskMinHeight: Int = MIN_HEIGHT) {
+        mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
+            taskId = TASK_ID
+            token = taskToken
+            minWidth = taskMinWidth
+            minHeight = taskMinHeight
+            defaultMinSize = DEFAULT_MIN
+            displayId = DISPLAY_ID
+            configuration.windowConfiguration.setBounds(STARTING_BOUNDS)
+        }
+    }
+
     companion object {
         private const val TASK_ID = 5
         private const val MIN_WIDTH = 10
         private const val MIN_HEIGHT = 10
+        private const val DESKTOP_MODE_MIN_WIDTH = 20
+        private const val DESKTOP_MODE_MIN_HEIGHT = 20
         private const val DENSITY_DPI = 20
         private const val DEFAULT_MIN = 40
         private const val DISPLAY_ID = 1
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 5464508..4dea5a7 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
@@ -27,10 +27,9 @@
 import android.annotation.NonNull;
 import android.graphics.Point;
 import android.graphics.Region;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.util.Size;
 
@@ -74,7 +73,7 @@
             TASK_SIZE.getHeight() + EDGE_RESIZE_THICKNESS / 2);
 
     @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     /**
      * Check that both groups of objects satisfy equals/hashcode within each group, and that each
@@ -144,10 +143,11 @@
 
     /**
      * Validate that with the flag enabled, the corner resize regions are the largest size, to
-     * capture all eligible input regardless of source (touch or cursor).
+     * capture all eligible input regardless of source (touchscreen or cursor).
+     * <p>Note that capturing input does not necessarily mean that the event will be handled.
      */
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testRegionUnion_edgeDragResizeEnabled_containsLargeCorners() {
         Region region = new Region();
         GEOMETRY.union(region);
@@ -164,7 +164,7 @@
      * size.
      */
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+    @DisableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testRegionUnion_edgeDragResizeDisabled_containsFineCorners() {
         Region region = new Region();
         GEOMETRY.union(region);
@@ -176,74 +176,114 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testCalculateControlType_edgeDragResizeEnabled_edges() {
-        // The input source (touch or cursor) shouldn't impact the edge resize size.
-        validateCtrlTypeForEdges(/* isTouch= */ false);
-        validateCtrlTypeForEdges(/* isTouch= */ true);
+        // The input source (touchscreen or cursor) shouldn't impact the edge resize size.
+        validateCtrlTypeForEdges(/* isTouchscreen= */ false, /* isEdgeResizePermitted= */ false);
+        validateCtrlTypeForEdges(/* isTouchscreen= */ true, /* isEdgeResizePermitted= */ false);
+        validateCtrlTypeForEdges(/* isTouchscreen= */ false, /* isEdgeResizePermitted= */ true);
+        validateCtrlTypeForEdges(/* isTouchscreen= */ true, /* isEdgeResizePermitted= */ true);
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+    @DisableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testCalculateControlType_edgeDragResizeDisabled_edges() {
-        // Edge resizing is not supported when the flag is disabled.
-        validateCtrlTypeForEdges(/* isTouch= */ false);
-        validateCtrlTypeForEdges(/* isTouch= */ false);
+        // Edge resizing is not supported for touchscreen input when the flag is disabled.
+        validateCtrlTypeForEdges(/* isTouchscreen= */ false, /* isEdgeResizePermitted= */ true);
+        validateCtrlTypeForEdges(/* isTouchscreen= */ true, /* isEdgeResizePermitted= */ false);
     }
 
-    private void validateCtrlTypeForEdges(boolean isTouch) {
-        assertThat(GEOMETRY.calculateCtrlType(isTouch, LEFT_EDGE_POINT.x,
-                LEFT_EDGE_POINT.y)).isEqualTo(CTRL_TYPE_LEFT);
-        assertThat(GEOMETRY.calculateCtrlType(isTouch, TOP_EDGE_POINT.x,
-                TOP_EDGE_POINT.y)).isEqualTo(CTRL_TYPE_TOP);
-        assertThat(GEOMETRY.calculateCtrlType(isTouch, RIGHT_EDGE_POINT.x,
-                RIGHT_EDGE_POINT.y)).isEqualTo(CTRL_TYPE_RIGHT);
-        assertThat(GEOMETRY.calculateCtrlType(isTouch, BOTTOM_EDGE_POINT.x,
-                BOTTOM_EDGE_POINT.y)).isEqualTo(CTRL_TYPE_BOTTOM);
+    private void validateCtrlTypeForEdges(boolean isTouchscreen, boolean isEdgeResizePermitted) {
+        assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                LEFT_EDGE_POINT.x, LEFT_EDGE_POINT.y)).isEqualTo(
+                        isEdgeResizePermitted ? CTRL_TYPE_LEFT : CTRL_TYPE_UNDEFINED);
+        assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                TOP_EDGE_POINT.x, TOP_EDGE_POINT.y)).isEqualTo(
+                        isEdgeResizePermitted ? CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
+        assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                RIGHT_EDGE_POINT.x, RIGHT_EDGE_POINT.y)).isEqualTo(
+                        isEdgeResizePermitted ? CTRL_TYPE_RIGHT : CTRL_TYPE_UNDEFINED);
+        assertThat(GEOMETRY.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                BOTTOM_EDGE_POINT.x, BOTTOM_EDGE_POINT.y)).isEqualTo(
+                        isEdgeResizePermitted ? CTRL_TYPE_BOTTOM : CTRL_TYPE_UNDEFINED);
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testCalculateControlType_edgeDragResizeEnabled_corners() {
         final TestPoints fineTestPoints = new TestPoints(TASK_SIZE, FINE_CORNER_SIZE / 2);
         final TestPoints largeCornerTestPoints = new TestPoints(TASK_SIZE, LARGE_CORNER_SIZE / 2);
 
         // When the flag is enabled, points within fine corners should pass regardless of touch or
         // not. Points outside fine corners should not pass when using a course input (non-touch).
-        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ true, true);
-        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ true, true);
-        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ false, true);
-        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ false, false);
+        // Edge resizing permitted (events from stylus/cursor) should have no impact on corners.
+        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ true, true);
+        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ true, true);
+        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ false, true);
+        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ false, true);
+        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ true, true);
+        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ true, false);
+        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ false, true);
+        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ false, false);
 
         // When the flag is enabled, points near the large corners should only pass when the point
         // is within the corner for large touch inputs.
-        largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ true, true);
-        largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ true,
-                false);
-        largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ false, false);
-        largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ false,
-                false);
+        largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ true, true);
+        largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ true, false);
+        largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ true, false);
+        largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ true, false);
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+    @DisableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
     public void testCalculateControlType_edgeDragResizeDisabled_corners() {
         final TestPoints fineTestPoints = new TestPoints(TASK_SIZE, FINE_CORNER_SIZE / 2);
         final TestPoints largeCornerTestPoints = new TestPoints(TASK_SIZE, LARGE_CORNER_SIZE / 2);
 
-        // When the flag is disabled, points within fine corners should pass only when touch.
-        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ true, true);
-        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ true, false);
-        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ false, false);
-        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ false, false);
+        // When the flag is disabled, points within fine corners should pass only from touchscreen.
+        // Edge resize permitted (indicating the event is from a cursor/stylus) should have no
+        // impact.
+        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ true, true);
+        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ true, false);
+        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ false, true);
+        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ false, false);
+
+        // Points within fine corners should never pass when not from touchscreen; expect edge
+        // resizing only.
+        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ true, false);
+        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ true, false);
+        fineTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ false, false);
+        fineTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ false, false);
 
         // When the flag is disabled, points near the large corners should never pass.
-        largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ true, false);
-        largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ true,
-                false);
-        largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouch= */ false, false);
-        largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouch= */ false,
-                false);
+        largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ true, false);
+        largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                true, /* isEdgeResizePermitted= */ true, false);
+        largeCornerTestPoints.validateCtrlTypeForInnerPoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ true, false);
+        largeCornerTestPoints.validateCtrlTypeForOutsidePoints(GEOMETRY, /* isTouchscreen= */
+                false, /* isEdgeResizePermitted= */ true, false);
     }
 
     /**
@@ -306,19 +346,20 @@
          * {@code @DragPositioningCallback.CtrlType}.
          */
         public void validateCtrlTypeForInnerPoints(@NonNull DragResizeWindowGeometry geometry,
-                boolean isTouch, boolean expectedWithinGeometry) {
-            assertThat(geometry.calculateCtrlType(isTouch, mTopLeftPoint.x,
-                    mTopLeftPoint.y)).isEqualTo(
+                boolean isTouchscreen, boolean isEdgeResizePermitted,
+                boolean expectedWithinGeometry) {
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                    mTopLeftPoint.x, mTopLeftPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouch, mTopRightPoint.x,
-                    mTopRightPoint.y)).isEqualTo(
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                    mTopRightPoint.x, mTopRightPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouch, mBottomLeftPoint.x,
-                    mBottomLeftPoint.y)).isEqualTo(
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                    mBottomLeftPoint.x, mBottomLeftPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouch, mBottomRightPoint.x,
-                    mBottomRightPoint.y)).isEqualTo(
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                    mBottomRightPoint.x, mBottomRightPoint.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
         }
@@ -328,19 +369,20 @@
          * {@code @DragPositioningCallback.CtrlType}.
          */
         public void validateCtrlTypeForOutsidePoints(@NonNull DragResizeWindowGeometry geometry,
-                boolean isTouch, boolean expectedWithinGeometry) {
-            assertThat(geometry.calculateCtrlType(isTouch, mTopLeftPointOutside.x,
-                    mTopLeftPointOutside.y)).isEqualTo(
+                boolean isTouchscreen, boolean isEdgeResizePermitted,
+                boolean expectedWithinGeometry) {
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                    mTopLeftPointOutside.x, mTopLeftPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouch, mTopRightPointOutside.x,
-                    mTopRightPointOutside.y)).isEqualTo(
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                    mTopRightPointOutside.x, mTopRightPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_RIGHT | CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouch, mBottomLeftPointOutside.x,
-                    mBottomLeftPointOutside.y)).isEqualTo(
+            assertThat(geometry.calculateCtrlType(isTouchscreen, isEdgeResizePermitted,
+                    mBottomLeftPointOutside.x, mBottomLeftPointOutside.y)).isEqualTo(
                     expectedWithinGeometry ? CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM
                             : CTRL_TYPE_UNDEFINED);
-            assertThat(geometry.calculateCtrlType(isTouch, mBottomRightPointOutside.x,
-                    mBottomRightPointOutside.y)).isEqualTo(
+            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/FluidResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
index 9174556..6667504 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/FluidResizeTaskPositionerTest.kt
@@ -2,6 +2,8 @@
 
 import android.app.ActivityManager
 import android.app.WindowConfiguration
+import android.content.Context
+import android.content.res.Resources
 import android.graphics.Point
 import android.graphics.Rect
 import android.os.IBinder
@@ -17,6 +19,7 @@
 import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
 import androidx.test.filters.SmallTest
+import com.android.wm.shell.R
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.DisplayController
@@ -83,7 +86,10 @@
     private lateinit var mockTransaction: SurfaceControl.Transaction
     @Mock
     private lateinit var mockTransitionBinder: IBinder
-
+    @Mock
+    private lateinit var mockContext: Context
+    @Mock
+    private lateinit var mockResources: Resources
     private lateinit var taskPositioner: FluidResizeTaskPositioner
 
     @Before
@@ -119,6 +125,12 @@
         }
         `when`(mockWindowDecoration.calculateValidDragArea()).thenReturn(VALID_DRAG_AREA)
         mockWindowDecoration.mDisplay = mockDisplay
+        mockWindowDecoration.mDecorWindowContext = mockContext
+        whenever(mockWindowDecoration.mDecorWindowContext.resources).thenReturn(mockResources)
+        whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_width))
+                .thenReturn(DESKTOP_MODE_MIN_WIDTH)
+        whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_height))
+                .thenReturn(DESKTOP_MODE_MIN_HEIGHT)
         whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
         whenever(mockTransitions.startTransition(anyInt(), any(), any()))
                 .doReturn(mockTransitionBinder)
@@ -788,6 +800,8 @@
         private const val TASK_ID = 5
         private const val MIN_WIDTH = 10
         private const val MIN_HEIGHT = 10
+        private const val DESKTOP_MODE_MIN_WIDTH = 20
+        private const val DESKTOP_MODE_MIN_HEIGHT = 20
         private const val DENSITY_DPI = 20
         private const val DEFAULT_MIN = 40
         private const val DISPLAY_ID = 1
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
index 5da57c5..a07be79 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt
@@ -150,7 +150,7 @@
     fun showVeil() {
         val veil = createResizeVeil()
 
-        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
 
         verify(mockTransaction).show(mockResizeVeilSurface)
         verify(mockTransaction).show(mockBackgroundSurface)
@@ -162,7 +162,7 @@
     fun showVeil_displayUnavailable_doesNotShow() {
         val veil = createResizeVeil(withDisplayAvailable = false)
 
-        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
 
         verify(mockTransaction, never()).show(mockResizeVeilSurface)
         verify(mockTransaction, never()).show(mockBackgroundSurface)
@@ -174,8 +174,8 @@
     fun showVeil_alreadyVisible_doesNotShowAgain() {
         val veil = createResizeVeil()
 
-        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
-        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), taskInfo, false /* fadeIn */)
 
         verify(mockTransaction, times(1)).show(mockResizeVeilSurface)
         verify(mockTransaction, times(1)).show(mockBackgroundSurface)
@@ -188,7 +188,13 @@
         val veil = createResizeVeil(parent = mock())
 
         val newParent = mock<SurfaceControl>()
-        veil.showVeil(mockTransaction, newParent, Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(
+            mockTransaction,
+            newParent,
+            Rect(0, 0, 100, 100),
+            taskInfo,
+            false /* fadeIn */
+        )
 
         verify(mockTransaction).reparent(mockResizeVeilSurface, newParent)
     }
@@ -212,11 +218,11 @@
             context,
             mockDisplayController,
             mockAppIcon,
-            taskInfo,
             parent,
             { mockTransaction },
             mockSurfaceControlBuilderFactory,
-            mockSurfaceControlViewHostFactory
+            mockSurfaceControlViewHostFactory,
+            taskInfo
         )
     }
 }
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 e73069a..f3603e1 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
@@ -32,6 +32,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
@@ -828,6 +829,36 @@
                 eq(mMockTaskSurface), anyInt(), anyInt());
     }
 
+    @Test
+    public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() {
+        final TestWindowDecoration windowDecor = createWindowDecoration(
+                new TestRunningTaskInfoBuilder().build());
+        mRelayoutParams.mApplyStartTransactionOnDraw = true;
+
+        windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult);
+
+        verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT);
+    }
+
+    @Test
+    public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() {
+        final TestWindowDecoration windowDecor = createWindowDecoration(
+                new TestRunningTaskInfoBuilder().build());
+        mRelayoutParams.mApplyStartTransactionOnDraw = true;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> windowDecor.updateViewHost(
+                        mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult));
+    }
+
+    @Test
+    public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() {
+        final TestWindowDecoration windowDecor = createWindowDecoration(
+                new TestRunningTaskInfoBuilder().build());
+        mRelayoutParams.mApplyStartTransactionOnDraw = false;
+
+        windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult);
+    }
 
     private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) {
         return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index b68143d..9435118 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -2451,10 +2451,10 @@
     const char script[4];
     const std::unordered_map<uint32_t, uint32_t>* map;
 } SCRIPT_PARENTS[] = {
+    {{'L', 'a', 't', 'n'}, &LATN_PARENTS},
     {{'A', 'r', 'a', 'b'}, &ARAB_PARENTS},
     {{'D', 'e', 'v', 'a'}, &DEVA_PARENTS},
     {{'H', 'a', 'n', 't'}, &HANT_PARENTS},
-    {{'L', 'a', 't', 'n'}, &LATN_PARENTS},
     {{'~', '~', '~', 'B'}, &___B_PARENTS},
 };
 
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index a3dd983..de9991a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2650,8 +2650,9 @@
                 return (mnc);
             }
         }
-
-        if (isLocaleBetterThan(o, requested)) {
+        // Cheaper to check for the empty locales here before calling the function
+        // as we often can skip it completely.
+        if (requested->locale && (locale || o.locale) && isLocaleBetterThan(o, requested)) {
             return true;
         }
 
@@ -7237,27 +7238,11 @@
 
 status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
     uint32_t res = *resId;
-    size_t packageId = Res_GETPACKAGE(res) + 1;
-
     if (!Res_VALIDID(res)) {
         // Cannot look up a null or invalid id, so no lookup needs to be done.
         return NO_ERROR;
     }
-
-    const auto alias_it = std::lower_bound(mAliasId.begin(), mAliasId.end(), res,
-        [](const AliasMap::value_type& pair, uint32_t val) { return pair.first < val; });
-    if (alias_it != mAliasId.end() && alias_it->first == res) {
-      // Rewrite the resource id to its alias resource id. Since the alias resource id is a
-      // compile-time id, it still needs to be resolved further.
-      res = alias_it->second;
-    }
-
-    if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
-        // No lookup needs to be done, app and framework package IDs are absolute.
-        *resId = res;
-        return NO_ERROR;
-    }
-
+    const size_t packageId = Res_GETPACKAGE(res) + 1;
     if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {
         // The package ID is 0x00. That means that a shared library is accessing
         // its own local resource.
@@ -7267,6 +7252,24 @@
         *resId = (0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);
         return NO_ERROR;
     }
+    // All aliases are coming from the framework, and usually have their own separate ID range,
+    // skipping the whole binary search is much more efficient than not finding anything.
+    if (packageId == SYS_PACKAGE_ID && !mAliasId.empty() &&
+            res >= mAliasId.front().first && res <= mAliasId.back().first) {
+        const auto alias_it = std::lower_bound(mAliasId.begin(), mAliasId.end(), res,
+                                               [](const AliasMap::value_type& pair,
+                                                  uint32_t val) { return pair.first < val; });
+        if (alias_it != mAliasId.end() && alias_it->first == res) {
+            // Rewrite the resource id to its alias resource id. Since the alias resource id is a
+            // compile-time id, it still needs to be resolved further.
+            res = alias_it->second;
+        }
+    }
+    if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
+        // No lookup needs to be done, app and framework package IDs are absolute.
+        *resId = res;
+        return NO_ERROR;
+    }
 
     // Do a proper lookup.
     uint8_t translatedId = mLookupTable[packageId];
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 7c1c5b4..341599e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -115,6 +115,7 @@
         "libharfbuzz_ng",
         "libminikin",
         "server_configurable_flags",
+        "libaconfig_storage_read_api_cc"
     ],
 
     static_libs: [
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 325bdd6..5d3bc89 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -39,6 +39,9 @@
 constexpr bool hdr_10bit_plus() {
     return false;
 }
+constexpr bool initialize_gl_always() {
+    return false;
+}
 }  // namespace hwui_flags
 #endif
 
@@ -257,5 +260,9 @@
     return drawingEnabled == DrawingEnabled::On;
 }
 
+bool Properties::initializeGlAlways() {
+    return base::GetBoolProperty(PROPERTY_INITIALIZE_GL_ALWAYS, hwui_flags::initialize_gl_always());
+}
+
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index c1510d9..d3176f6 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -229,6 +229,11 @@
 
 #define PROPERTY_8BIT_HDR_HEADROOM "debug.hwui.8bit_hdr_headroom"
 
+/**
+ * Whether to initialize GL even when HWUI is running Vulkan.
+ */
+#define PROPERTY_INITIALIZE_GL_ALWAYS "debug.hwui.initialize_gl_always"
+
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
 ///////////////////////////////////////////////////////////////////////////////
@@ -368,6 +373,8 @@
     static bool isDrawingEnabled();
     static void setDrawingEnabled(bool enable);
 
+    static bool initializeGlAlways();
+
 private:
     static StretchEffectBehavior stretchEffectBehavior;
     static ProfileType sProfileType;
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 50f8b39..cd3ae53 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -90,3 +90,10 @@
   description: "Add canvas#drawRegion API"
   bug: "318612129"
 }
+
+flag {
+  name: "initialize_gl_always"
+  namespace: "core_graphics"
+  description: "Initialize GL even when HWUI is set to use Vulkan. This improves app startup time for apps using GL."
+  bug: "335172671"
+}
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 6ace396..15b2bac 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -192,5 +192,14 @@
         // Preload Vulkan driver if HWUI renders with Vulkan backend.
         uint32_t apiVersion;
         vkEnumerateInstanceVersion(&apiVersion);
+
+        if (Properties::initializeGlAlways()) {
+            // Even though HWUI is rendering with Vulkan, some apps still use
+            // GL. Preload GL driver just in case. Since this happens prior to
+            // forking from the zygote, apps that do not use GL are unaffected.
+            // Any memory that (E)GL uses for this call is in shared memory,
+            // and this call only happens once.
+            eglGetDisplay(EGL_DEFAULT_DISPLAY);
+        }
     }
 }
diff --git a/libs/hwui/effects/GainmapRenderer.cpp b/libs/hwui/effects/GainmapRenderer.cpp
index 0a30c6c..eac0360 100644
--- a/libs/hwui/effects/GainmapRenderer.cpp
+++ b/libs/hwui/effects/GainmapRenderer.cpp
@@ -96,6 +96,7 @@
 #ifdef __ANDROID__
 
 static constexpr char gGainmapSKSL[] = R"SKSL(
+    uniform shader linearBase;
     uniform shader base;
     uniform shader gainmap;
     uniform colorFilter workingSpaceToLinearSrgb;
@@ -117,7 +118,11 @@
     }
 
     half4 main(float2 coord) {
-        half4 S = base.eval(coord);
+        if (W == 0.0) {
+            return base.eval(coord);
+        }
+
+        half4 S = linearBase.eval(coord);
         half4 G = gainmap.eval(coord);
         if (gainmapIsAlpha == 1) {
             G = half4(G.a, G.a, G.a, 1.0);
@@ -186,8 +191,10 @@
                 SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace);
 
         // The base image shader will convert into the color space in which the gainmap is applied.
-        auto baseImageShader = baseImage->makeRawShader(tileModeX, tileModeY, samplingOptions)
-                                       ->makeWithColorFilter(colorXformSdrToGainmap);
+        auto linearBaseImageShader = baseImage->makeRawShader(tileModeX, tileModeY, samplingOptions)
+                                             ->makeWithColorFilter(colorXformSdrToGainmap);
+
+        auto baseImageShader = baseImage->makeShader(tileModeX, tileModeY, samplingOptions);
 
         // The gainmap image shader will ignore any color space that the gainmap has.
         const SkMatrix gainmapRectToDstRect =
@@ -201,6 +208,7 @@
         auto colorXformGainmapToDst = SkColorFilterPriv::MakeColorSpaceXform(
                 gainmapMathColorSpace, SkColorSpace::MakeSRGBLinear());
 
+        mBuilder.child("linearBase") = std::move(linearBaseImageShader);
         mBuilder.child("base") = std::move(baseImageShader);
         mBuilder.child("gainmap") = std::move(gainmapImageShader);
         mBuilder.child("workingSpaceToLinearSrgb") = std::move(colorXformGainmapToDst);
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index 07e97f8..a88139d 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -583,6 +583,16 @@
                             transferParams.a, transferParams.b, transferParams.c, transferParams.d,
                             transferParams.e, transferParams.f, transferParams.g);
 
+    // Some transfer functions that are considered valid by Skia are not
+    // accepted by android.graphics.
+    if (hasException(env)) {
+        // Callers (e.g. Bitmap#getColorSpace) are not expected to throw an
+        // Exception, so clear it and return null, which is a documented
+        // possibility.
+        env->ExceptionClear();
+        return nullptr;
+    }
+
     jfloatArray xyzArray = env->NewFloatArray(9);
     jfloat xyz[9] = {
             xyzMatrix.vals[0][0],
diff --git a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
index 0b39a9a..df4b903 100644
--- a/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
+++ b/location/lib/java/com/android/location/provider/SignificantPlaceProvider.java
@@ -21,17 +21,22 @@
 import android.hardware.location.ISignificantPlaceProvider;
 import android.hardware.location.ISignificantPlaceProviderManager;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
 /** @hide */
-public class SignificantPlaceProvider {
+public abstract class SignificantPlaceProvider {
 
     public static final String ACTION = TrustManager.ACTION_BIND_SIGNIFICANT_PLACE_PROVIDER;
 
+    private static final String TAG = "SignificantPlaceProvider";
+
     private final IBinder mBinder;
 
     // write locked on mBinder, read lock is optional depending on atomicity requirements
@@ -69,6 +74,9 @@
         }
     }
 
+    /** Invoked when some client has checked whether the device is in a significant place. */
+    public abstract void onSignificantPlaceCheck();
+
     private final class Service extends ISignificantPlaceProvider.Stub {
 
         Service() {}
@@ -76,7 +84,7 @@
         @Override
         public void setSignificantPlaceProviderManager(ISignificantPlaceProviderManager manager) {
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                return;
+                throw new SecurityException();
             }
 
             synchronized (mBinder) {
@@ -91,5 +99,22 @@
                 mManager = manager;
             }
         }
+
+        @Override
+        public void onSignificantPlaceCheck() {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException();
+            }
+
+            try {
+                SignificantPlaceProvider.this.onSignificantPlaceCheck();
+            } catch (RuntimeException e) {
+                // exceptions on one-way binder threads are dropped - move to a different thread
+                Log.w(TAG, e);
+                new Handler(Looper.getMainLooper()).post(() -> {
+                    throw new AssertionError(e);
+                });
+            }
+        }
     }
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index efbf8da..eeb4853 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -52,6 +52,7 @@
     // Methods for MediaRouter2
     List<MediaRoute2Info> getSystemRoutes(String callerPackageName, boolean isProxyRouter);
     RoutingSessionInfo getSystemSessionInfo();
+    boolean showMediaOutputSwitcherWithRouter2(String packageName);
 
     void registerRouter2(IMediaRouter2 router, String packageName);
     void unregisterRouter2(IMediaRouter2 router);
@@ -97,5 +98,5 @@
     void setSessionVolumeWithManager(IMediaRouter2Manager manager, int requestId,
             String sessionId, int volume);
     void releaseSessionWithManager(IMediaRouter2Manager manager, int requestId, String sessionId);
-    boolean showMediaOutputSwitcher(String packageName);
+    boolean showMediaOutputSwitcherWithProxyRouter(IMediaRouter2Manager manager);
 }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 5672cd5..0667bfd 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -2666,8 +2666,11 @@
 
         @Override
         public boolean showSystemOutputSwitcher() {
-            throw new UnsupportedOperationException(
-                    "Cannot show system output switcher from a privileged router.");
+            try {
+                return mMediaRouterService.showMediaOutputSwitcherWithProxyRouter(mClient);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
+            }
         }
 
         /** Gets the list of all discovered routes. */
@@ -3539,7 +3542,7 @@
         public boolean showSystemOutputSwitcher() {
             synchronized (mLock) {
                 try {
-                    return mMediaRouterService.showMediaOutputSwitcher(mImpl.getPackageName());
+                    return mMediaRouterService.showMediaOutputSwitcherWithRouter2(mPackageName);
                 } catch (RemoteException ex) {
                     ex.rethrowFromSystemServer();
                 }
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
old mode 100755
new mode 100644
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
old mode 100755
new mode 100644
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
old mode 100755
new mode 100644
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
old mode 100755
new mode 100644
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
old mode 100755
new mode 100644
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 8874ff5..e619e1c 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -127,6 +127,9 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
+
+    // TODO(b/330503129) Workaround build breakage.
+    lto_O0: true,
 }
 
 cc_library_shared {
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index cf5059c..7caa9e4 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -44,4 +44,7 @@
         "-Wunreachable-code",
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
+
+    // TODO(b/330503129) Workaround LTO build breakage.
+    lto_O0: true,
 }
diff --git a/media/lib/remotedisplay/OWNERS b/media/lib/remotedisplay/OWNERS
index 7e7335d..6f6b624 100644
--- a/media/lib/remotedisplay/OWNERS
+++ b/media/lib/remotedisplay/OWNERS
@@ -1 +1 @@
-michaelwr@google.com
+file:/services/core/java/com/android/server/display/OWNERS
\ No newline at end of file
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 8945bd1..c4c4102 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -41,6 +41,7 @@
         "-Wextra",
         "-Wunused",
         "-Wunreachable-code",
+        "-Wthread-safety",
     ],
 }
 
diff --git a/native/android/input.cpp b/native/android/input.cpp
index 0a22314..9a8cda3 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -100,7 +100,8 @@
 }
 
 int32_t AMotionEvent_getFlags(const AInputEvent* motion_event) {
-    return static_cast<const MotionEvent*>(motion_event)->getFlags();
+    return static_cast<const MotionEvent*>(motion_event)->getFlags() &
+            ~AMOTION_EVENT_PRIVATE_FLAG_MASK;
 }
 
 int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event) {
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 02d72ad..7e5bef1 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -23,6 +23,7 @@
 #include <aidl/android/os/IHintManager.h>
 #include <aidl/android/os/IHintSession.h>
 #include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
 #include <android/binder_manager.h>
 #include <android/binder_status.h>
 #include <android/performance_hint.h>
@@ -51,6 +52,9 @@
 constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
 struct AWorkDuration : public hal::WorkDuration {};
 
+// Shared lock for the whole PerformanceHintManager and sessions
+static std::mutex sHintMutex = std::mutex{};
+
 struct APerformanceHintManager {
 public:
     static APerformanceHintManager* getInstance();
@@ -108,26 +112,26 @@
     // HAL preferred update rate
     const int64_t mPreferredRateNanos;
     // Target duration for choosing update rate
-    int64_t mTargetDurationNanos;
+    int64_t mTargetDurationNanos GUARDED_BY(sHintMutex);
     // First target hit timestamp
-    int64_t mFirstTargetMetTimestamp;
+    int64_t mFirstTargetMetTimestamp GUARDED_BY(sHintMutex);
     // Last target hit timestamp
-    int64_t mLastTargetMetTimestamp;
+    int64_t mLastTargetMetTimestamp GUARDED_BY(sHintMutex);
     // Last hint reported from sendHint indexed by hint value
-    std::vector<int64_t> mLastHintSentTimestamp;
+    std::vector<int64_t> mLastHintSentTimestamp GUARDED_BY(sHintMutex);
     // Cached samples
-    std::vector<hal::WorkDuration> mActualWorkDurations;
-    std::string mSessionName;
-    static int64_t sIDCounter;
+    std::vector<hal::WorkDuration> mActualWorkDurations GUARDED_BY(sHintMutex);
+    std::string mSessionName GUARDED_BY(sHintMutex);
+    static int64_t sIDCounter GUARDED_BY(sHintMutex);
     // The most recent set of thread IDs
-    std::vector<int32_t> mLastThreadIDs;
-    std::optional<hal::SessionConfig> mSessionConfig;
+    std::vector<int32_t> mLastThreadIDs GUARDED_BY(sHintMutex);
+    std::optional<hal::SessionConfig> mSessionConfig GUARDED_BY(sHintMutex);
     // Tracing helpers
-    void traceThreads(std::vector<int32_t>& tids);
-    void tracePowerEfficient(bool powerEfficient);
-    void traceActualDuration(int64_t actualDuration);
-    void traceBatchSize(size_t batchSize);
-    void traceTargetDuration(int64_t targetDuration);
+    void traceThreads(std::vector<int32_t>& tids) REQUIRES(sHintMutex);
+    void tracePowerEfficient(bool powerEfficient) REQUIRES(sHintMutex);
+    void traceActualDuration(int64_t actualDuration) REQUIRES(sHintMutex);
+    void traceBatchSize(size_t batchSize) REQUIRES(sHintMutex);
+    void traceTargetDuration(int64_t targetDuration) REQUIRES(sHintMutex);
 };
 
 static std::shared_ptr<IHintManager>* gIHintManagerForTesting = nullptr;
@@ -192,6 +196,7 @@
     }
     auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos,
                                            initialTargetWorkDurationNanos, sessionConfig);
+    std::scoped_lock lock(sHintMutex);
     out->traceThreads(tids);
     out->traceTargetDuration(initialTargetWorkDurationNanos);
     out->tracePowerEfficient(false);
@@ -219,6 +224,7 @@
     if (sessionConfig->id > INT32_MAX) {
         ALOGE("Session ID too large, must fit 32-bit integer");
     }
+    std::scoped_lock lock(sHintMutex);
     constexpr int numEnums =
             ndk::enum_range<hal::SessionHint>().end() - ndk::enum_range<hal::SessionHint>().begin();
     mLastHintSentTimestamp = std::vector<int64_t>(numEnums, 0);
@@ -244,6 +250,7 @@
               ret.getMessage());
         return EPIPE;
     }
+    std::scoped_lock lock(sHintMutex);
     mTargetDurationNanos = targetDurationNanos;
     /**
      * Most of the workload is target_duration dependent, so now clear the cached samples
@@ -267,6 +274,7 @@
 }
 
 int APerformanceHintSession::sendHint(SessionHint hint) {
+    std::scoped_lock lock(sHintMutex);
     if (hint < 0 || hint >= static_cast<int32_t>(mLastHintSentTimestamp.size())) {
         ALOGE("%s: invalid session hint %d", __FUNCTION__, hint);
         return EINVAL;
@@ -305,6 +313,7 @@
         return EPIPE;
     }
 
+    std::scoped_lock lock(sHintMutex);
     traceThreads(tids);
 
     return 0;
@@ -343,6 +352,7 @@
               ret.getMessage());
         return EPIPE;
     }
+    std::scoped_lock lock(sHintMutex);
     tracePowerEfficient(enabled);
     return OK;
 }
@@ -355,6 +365,7 @@
     int64_t actualTotalDurationNanos = workDuration->durationNanos;
     int64_t now = uptimeNanos();
     workDuration->timeStampNanos = now;
+    std::scoped_lock lock(sHintMutex);
     traceActualDuration(workDuration->durationNanos);
     mActualWorkDurations.push_back(std::move(*workDuration));
 
diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp
index b43f2f16..f7a3537 100644
--- a/native/android/thermal.cpp
+++ b/native/android/thermal.cpp
@@ -99,21 +99,21 @@
       : mThermalSvc(std::move(service)), mServiceListener(nullptr) {}
 
 AThermalManager::~AThermalManager() {
-    std::unique_lock<std::mutex> listenerLock(mListenerMutex);
-
-    mListeners.clear();
-    if (mServiceListener != nullptr) {
-        bool success = false;
-        mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
-        mServiceListener = nullptr;
+    {
+        std::scoped_lock<std::mutex> listenerLock(mListenerMutex);
+        mListeners.clear();
+        if (mServiceListener != nullptr) {
+            bool success = false;
+            mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
+            mServiceListener = nullptr;
+        }
     }
-    listenerLock.unlock();
-    std::unique_lock<std::mutex> lock(mThresholdsMutex);
+    std::scoped_lock<std::mutex> lock(mThresholdsMutex);
     delete[] mThresholds;
 }
 
 status_t AThermalManager::notifyStateChange(int32_t status) {
-    std::unique_lock<std::mutex> lock(mListenerMutex);
+    std::scoped_lock<std::mutex> lock(mListenerMutex);
     AThermalStatus thermalStatus = static_cast<AThermalStatus>(status);
 
     for (auto listener : mListeners) {
@@ -123,7 +123,7 @@
 }
 
 status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) {
-    std::unique_lock<std::mutex> lock(mListenerMutex);
+    std::scoped_lock<std::mutex> lock(mListenerMutex);
 
     if (callback == nullptr) {
         // Callback can not be nullptr
@@ -157,7 +157,7 @@
 }
 
 status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) {
-    std::unique_lock<std::mutex> lock(mListenerMutex);
+    std::scoped_lock<std::mutex> lock(mListenerMutex);
 
     auto it = std::remove_if(mListeners.begin(),
                              mListeners.end(),
@@ -216,7 +216,7 @@
 
 status_t AThermalManager::getThermalHeadroomThresholds(const AThermalHeadroomThreshold **result,
                                                        size_t *size) {
-    std::unique_lock<std::mutex> lock(mThresholdsMutex);
+    std::scoped_lock<std::mutex> lock(mThresholdsMutex);
     if (mThresholds == nullptr) {
         auto thresholds = std::make_unique<std::vector<float>>();
         binder::Status ret = mThermalSvc->getThermalHeadroomThresholds(thresholds.get());
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 8ea4632..746c280 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -111,6 +111,7 @@
             "allocator_may_return_null = 1",
         ],
     },
+    dictionary: "fuzz/imagedecoder_fuzzer.dict",
     host_supported: true,
 }
 
diff --git a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
index 838bf3f..6743997 100644
--- a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
+++ b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
@@ -15,32 +15,15 @@
  */
 
 #include <android/imagedecoder.h>
-
 #include <binder/IPCThreadState.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <cstdlib>
-#include <memory>
+#include <fuzzer/FuzzedDataProvider.h>
 
 #ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
 #include <fuzz/png_mutator.h>
 #endif
 
-struct DecoderDeleter {
-    void operator()(AImageDecoder* decoder) const { AImageDecoder_delete(decoder); }
-};
-
-using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>;
-
-static DecoderPointer makeDecoder(const uint8_t* data, size_t size) {
-    AImageDecoder* decoder = nullptr;
-    int result = AImageDecoder_createFromBuffer(data, size, &decoder);
-    if (result != ANDROID_IMAGE_DECODER_SUCCESS) {
-        // This was not a valid image.
-        return nullptr;
-    }
-    return DecoderPointer(decoder);
-}
+constexpr int32_t kMaxDimension = 5000;
+constexpr int32_t kMinDimension = 0;
 
 struct PixelFreer {
     void operator()(void* pixels) const { std::free(pixels); }
@@ -48,41 +31,113 @@
 
 using PixelPointer = std::unique_ptr<void, PixelFreer>;
 
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    // Without this call, decoding HEIF may time out on binder IPC calls.
-    android::ProcessState::self()->startThreadPool();
+AImageDecoder* init(const uint8_t* data, size_t size, bool useFileDescriptor) {
+    AImageDecoder* decoder = nullptr;
+    if (useFileDescriptor) {
+        constexpr char testFd[] = "tempFd";
+        int32_t fileDesc = open(testFd, O_RDWR | O_CREAT | O_TRUNC);
+        write(fileDesc, data, size);
+        AImageDecoder_createFromFd(fileDesc, &decoder);
+        close(fileDesc);
+    } else {
+        AImageDecoder_createFromBuffer(data, size, &decoder);
+    }
+    return decoder;
+}
 
-    DecoderPointer decoder = makeDecoder(data, size);
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size);
+    /**
+     * Use maximum of 80% of buffer for creating decoder and save at least
+     * 20% buffer for fuzzing other APIs
+     */
+    const int32_t dataSize = dataProvider.ConsumeIntegralInRange<int32_t>(0, (size * 80) / 100);
+    std::vector<uint8_t> inputBuffer = dataProvider.ConsumeBytes<uint8_t>(dataSize);
+    AImageDecoder* decoder =
+            init(inputBuffer.data(), inputBuffer.size(), dataProvider.ConsumeBool());
     if (!decoder) {
         return 0;
     }
-
-    const AImageDecoderHeaderInfo* info = AImageDecoder_getHeaderInfo(decoder.get());
-    int32_t width = AImageDecoderHeaderInfo_getWidth(info);
-    int32_t height = AImageDecoderHeaderInfo_getHeight(info);
-
-    // Set an arbitrary limit on the size of an image. The fuzzer runs with a
-    // limited amount of memory, and keeping this allocation small allows the
-    // fuzzer to continue running to try to find more serious problems. This
-    // size is large enough to hold a photo taken by a current gen phone.
-    constexpr int32_t kMaxDimension = 5000;
-    if (width > kMaxDimension || height > kMaxDimension) {
-        return 0;
+    const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder);
+    AImageDecoderFrameInfo* frameInfo = AImageDecoderFrameInfo_create();
+    int32_t height = AImageDecoderHeaderInfo_getHeight(headerInfo);
+    int32_t width = AImageDecoderHeaderInfo_getWidth(headerInfo);
+    while (dataProvider.remaining_bytes()) {
+        auto invokeImageApi = dataProvider.PickValueInArray<const std::function<void()>>({
+                [&]() {
+                    int32_t testHeight =
+                            dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension,
+                                                                         kMaxDimension);
+                    int32_t testWidth = dataProvider.ConsumeIntegralInRange<int32_t>(kMinDimension,
+                                                                                     kMaxDimension);
+                    int32_t result = AImageDecoder_setTargetSize(decoder, testHeight, testWidth);
+                    if (result == ANDROID_IMAGE_DECODER_SUCCESS) {
+                        height = testHeight;
+                        width = testWidth;
+                    }
+                },
+                [&]() {
+                    const bool required = dataProvider.ConsumeBool();
+                    AImageDecoder_setUnpremultipliedRequired(decoder, required);
+                },
+                [&]() {
+                    AImageDecoder_setAndroidBitmapFormat(
+                            decoder,
+                            dataProvider.ConsumeIntegralInRange<
+                                    int32_t>(ANDROID_BITMAP_FORMAT_NONE,
+                                             ANDROID_BITMAP_FORMAT_RGBA_1010102) /* format */);
+                },
+                [&]() {
+                    AImageDecoder_setDataSpace(decoder,
+                                               dataProvider
+                                                       .ConsumeIntegral<int32_t>() /* dataspace */);
+                },
+                [&]() {
+                    ARect rect{dataProvider.ConsumeIntegral<int32_t>() /* left */,
+                               dataProvider.ConsumeIntegral<int32_t>() /* top */,
+                               dataProvider.ConsumeIntegral<int32_t>() /* right */,
+                               dataProvider.ConsumeIntegral<int32_t>() /* bottom */};
+                    AImageDecoder_setCrop(decoder, rect);
+                },
+                [&]() { AImageDecoderHeaderInfo_getWidth(headerInfo); },
+                [&]() { AImageDecoderHeaderInfo_getMimeType(headerInfo); },
+                [&]() { AImageDecoderHeaderInfo_getAlphaFlags(headerInfo); },
+                [&]() { AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); },
+                [&]() {
+                    int32_t tempHeight;
+                    int32_t tempWidth;
+                    AImageDecoder_computeSampledSize(decoder,
+                                                     dataProvider.ConsumeIntegral<
+                                                             int>() /* sampleSize */,
+                                                     &tempWidth, &tempHeight);
+                },
+                [&]() { AImageDecoderHeaderInfo_getDataSpace(headerInfo); },
+                [&]() { AImageDecoder_getRepeatCount(decoder); },
+                [&]() { AImageDecoder_getFrameInfo(decoder, frameInfo); },
+                [&]() { AImageDecoderFrameInfo_getDuration(frameInfo); },
+                [&]() { AImageDecoderFrameInfo_hasAlphaWithinBounds(frameInfo); },
+                [&]() { AImageDecoderFrameInfo_getDisposeOp(frameInfo); },
+                [&]() { AImageDecoderFrameInfo_getBlendOp(frameInfo); },
+                [&]() {
+                    AImageDecoder_setInternallyHandleDisposePrevious(
+                            decoder, dataProvider.ConsumeBool() /* handle */);
+                },
+                [&]() { AImageDecoder_rewind(decoder); },
+                [&]() { AImageDecoder_advanceFrame(decoder); },
+                [&]() {
+                    size_t stride = AImageDecoder_getMinimumStride(decoder);
+                    size_t pixelSize = height * stride;
+                    auto pixels = PixelPointer(std::malloc(pixelSize));
+                    if (!pixels.get()) {
+                        return;
+                    }
+                    AImageDecoder_decodeImage(decoder, pixels.get(), stride, pixelSize);
+                },
+        });
+        invokeImageApi();
     }
 
-    size_t stride = AImageDecoder_getMinimumStride(decoder.get());
-    size_t pixelSize = height * stride;
-    auto pixels = PixelPointer(std::malloc(pixelSize));
-    if (!pixels.get()) {
-        return 0;
-    }
-
-    while (true) {
-        int result = AImageDecoder_decodeImage(decoder.get(), pixels.get(), stride, pixelSize);
-        if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
-
-        result = AImageDecoder_advanceFrame(decoder.get());
-        if (result != ANDROID_IMAGE_DECODER_SUCCESS) break;
-    }
+    AImageDecoderFrameInfo_delete(frameInfo);
+    AImageDecoder_delete(decoder);
     return 0;
 }
diff --git a/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict
new file mode 100644
index 0000000..5b54a0e
--- /dev/null
+++ b/native/graphics/jni/fuzz/imagedecoder_fuzzer.dict
@@ -0,0 +1,7 @@
+kw1="\x89\x50\x4E\x47"
+kw2="\xff\xD8\xFF"
+kw4="\x52\x49\x46\x46"
+kw5="\x00\x00\x01\x00"
+kw6="\x47\x49\x46\x08"
+kw7="ftyp"
+kw8="\x04\x00\x00\x00"
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 055ccbc..3375e18c 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -57,7 +57,9 @@
 
   @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
+    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.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);
   }
 
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 7150b54..fd77820 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -110,4 +110,6 @@
     void registerOemExtensionCallback(INfcOemExtensionCallback callbacks);
     void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks);
     void clearPreference();
+    void setScreenState();
+    void checkFirmware();
 }
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 1eff58c..f6138a6 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -141,6 +141,34 @@
         }
     }
 
+    /**
+     * Get the screen state from system and set it to current screen state.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void synchronizeScreenState() {
+        try {
+            NfcAdapter.sService.setScreenState();
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
+     * Check if the firmware needs updating.
+     *
+     * <p>If an update is needed, a firmware will be triggered when NFC is disabled.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void maybeTriggerFirmwareUpdate() {
+        try {
+            NfcAdapter.sService.checkFirmware();
+        } catch (RemoteException e) {
+            mAdapter.attemptDeadServiceRecovery(e);
+        }
+    }
+
     private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
         @Override
         public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index a470f93..3cf0a4d 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -412,7 +412,7 @@
                             false);
                     if (!mOnHost && !autoTransact) {
                         Log.e(TAG, "Ignoring polling-loop-filter " + plf
-                                + " for offhost service that isn't autoTranact");
+                                + " for offhost service that isn't autoTransact");
                     } else {
                         mAutoTransact.put(plf, autoTransact);
                     }
@@ -429,7 +429,7 @@
                             false);
                     if (!mOnHost && !autoTransact) {
                         Log.e(TAG, "Ignoring polling-loop-filter " + plf
-                                + " for offhost service that isn't autoTranact");
+                                + " for offhost service that isn't autoTransact");
                     } else {
                         mAutoTransactPatterns.put(Pattern.compile(plf), autoTransact);
                     }
@@ -1028,6 +1028,9 @@
         pw.println("    Settings Activity: " + mSettingsActivityName);
         pw.println("    Requires Device Unlock: " + mRequiresDeviceUnlock);
         pw.println("    Requires Device ScreenOn: " + mRequiresDeviceScreenOn);
+        pw.println("    Should Default to Observe Mode: " + mShouldDefaultToObserveMode);
+        pw.println("    Auto-Transact Mapping: " + mAutoTransact);
+        pw.println("    Auto-Transact Patterns: " + mAutoTransactPatterns);
     }
 
 
@@ -1081,6 +1084,27 @@
             proto.end(token);
         }
         proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName);
+        proto.write(ApduServiceInfoProto.SHOULD_DEFAULT_TO_OBSERVE_MODE,
+                mShouldDefaultToObserveMode);
+        {
+            long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_MAPPING);
+            for (Map.Entry<String, Boolean> entry : mAutoTransact.entrySet()) {
+                proto.write(ApduServiceInfoProto.AutoTransactMapping.AID, entry.getKey());
+                proto.write(ApduServiceInfoProto.AutoTransactMapping.SHOULD_AUTO_TRANSACT,
+                        entry.getValue());
+            }
+            proto.end(token);
+        }
+        {
+            long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_PATTERNS);
+            for (Map.Entry<Pattern, Boolean> entry : mAutoTransactPatterns.entrySet()) {
+                proto.write(ApduServiceInfoProto.AutoTransactPattern.REGEXP_PATTERN,
+                        entry.getKey().pattern());
+                proto.write(ApduServiceInfoProto.AutoTransactPattern.SHOULD_AUTO_TRANSACT,
+                        entry.getValue());
+            }
+            proto.end(token);
+        }
     }
 
     private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 40d3be5..3575ff3 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -59,7 +59,7 @@
     <string name="permission_microphone" msgid="2152206421428732949">"Microfono"</string>
     <string name="permission_call_logs" msgid="5546761417694586041">"Registri chiamate"</string>
     <string name="permission_nearby_devices" msgid="7530973297737123481">"Dispositivi nelle vicinanze"</string>
-    <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia uscita conten. multim."</string>
+    <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia uscita  multimediale"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto e contenuti multimediali"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifiche"</string>
     <string name="permission_app_streaming" msgid="6009695219091526422">"App"</string>
diff --git a/packages/CrashRecovery/aconfig/flags.aconfig b/packages/CrashRecovery/aconfig/flags.aconfig
index 8cdef38..80412321 100644
--- a/packages/CrashRecovery/aconfig/flags.aconfig
+++ b/packages/CrashRecovery/aconfig/flags.aconfig
@@ -12,15 +12,22 @@
 flag {
     name: "enable_crashrecovery"
     is_exported: true
-    namespace: "crashrecovery"
+    namespace: "modularization"
     description: "Enables various dependencies of crashrecovery module"
     bug: "289203818"
 }
 
 flag {
     name: "allow_rescue_party_flag_resets"
-    namespace: "crashrecovery"
+    namespace: "modularization"
     description: "Enables rescue party flag resets"
     bug: "287618292"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "reenable_settings_resets"
+    namespace: "modularization"
+    description: "Re-enables settings resets only, deletes flag resets"
+    bug: "333847376"
+}
diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml
index bfc31a1..897f444 100644
--- a/packages/CredentialManager/res/values-kn/strings.xml
+++ b/packages/CredentialManager/res/values-kn/strings.xml
@@ -20,7 +20,7 @@
     <string name="app_name" msgid="4539824758261855508">"ರುಜುವಾತು ನಿರ್ವಾಹಕ"</string>
     <string name="string_cancel" msgid="6369133483981306063">"ರದ್ದುಗೊಳಿಸಿ"</string>
     <string name="string_continue" msgid="1346732695941131882">"ಮುಂದುವರಿಸಿ"</string>
-    <string name="string_more_options" msgid="2763852250269945472">"ಬೇರೆ ವಿಧಾನದಲ್ಲಿ ಉಳಿಸಿ"</string>
+    <string name="string_more_options" msgid="2763852250269945472">"ಬೇರೆ ವಿಧಾನದಲ್ಲಿ ಸೇವ್ ಮಾಡಿ"</string>
     <string name="string_learn_more" msgid="4541600451688392447">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
     <string name="content_description_show_password" msgid="3283502010388521607">"ಪಾಸ್‌ವರ್ಡ್ ತೋರಿಸಿ"</string>
     <string name="content_description_hide_password" msgid="6841375971631767996">"ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಮರೆಮಾಡಿ"</string>
@@ -48,7 +48,7 @@
     <string name="passwords" msgid="5419394230391253816">"ಪಾಸ್‌ವರ್ಡ್‌ಗಳು"</string>
     <string name="sign_ins" msgid="4710739369149469208">"ಸೈನ್-ಇನ್‌ಗಳು"</string>
     <string name="sign_in_info" msgid="2627704710674232328">"ಸೈನ್-ಇನ್ ಮಾಹಿತಿ"</string>
-    <string name="save_credential_to_title" msgid="3172811692275634301">"ಇಲ್ಲಿಗೆ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ಅನ್ನು ಉಳಿಸಿ"</string>
+    <string name="save_credential_to_title" msgid="3172811692275634301">"ಇಲ್ಲಿಗೆ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> ಅನ್ನು ಸೇವ್ ಮಾಡಿ"</string>
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"ಇನ್ನೊಂದು ಸಾಧನದಲ್ಲಿ ಪಾಸ್‌ಕೀ ಅನ್ನು ರಚಿಸಬೇಕೆ?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"ಇನ್ನೊಂದು ಸಾಧನದಲ್ಲಿ ಪಾಸ್‌ವರ್ಡ್ ಉಳಿಸಬೇಕೆ?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"ಮತ್ತೊಂದು ಸಾಧನದಲ್ಲಿ ಸೈನ್-ಇನ್ ಅನ್ನು ಉಳಿಸಬೇಕೆ?"</string>
diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml
index bc8a969..91385c6 100644
--- a/packages/CredentialManager/res/values-pt-rBR/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml
@@ -48,7 +48,7 @@
     <string name="passwords" msgid="5419394230391253816">"senhas"</string>
     <string name="sign_ins" msgid="4710739369149469208">"logins"</string>
     <string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string>
-    <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string>
+    <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string>
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string>
@@ -57,9 +57,9 @@
     <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string>
     <string name="settings" msgid="6536394145760913145">"Configurações"</string>
     <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
-    <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
-    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string>
-    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string>
     <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string>
     <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string>
     <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml
index bc8a969..91385c6 100644
--- a/packages/CredentialManager/res/values-pt/strings.xml
+++ b/packages/CredentialManager/res/values-pt/strings.xml
@@ -48,7 +48,7 @@
     <string name="passwords" msgid="5419394230391253816">"senhas"</string>
     <string name="sign_ins" msgid="4710739369149469208">"logins"</string>
     <string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string>
-    <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string>
+    <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string>
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string>
@@ -57,9 +57,9 @@
     <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string>
     <string name="settings" msgid="6536394145760913145">"Configurações"</string>
     <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
-    <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
-    <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string>
-    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string>
+    <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string>
+    <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string>
+    <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string>
     <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string>
     <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string>
     <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index c477f30..08846f0 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -45,8 +45,8 @@
 import androidx.credentials.CreateCustomCredentialRequest
 import androidx.credentials.CreatePasswordRequest
 import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.CredentialOption
 import androidx.credentials.PasswordCredential
-import androidx.credentials.PriorityHints
 import androidx.credentials.PublicKeyCredential
 import androidx.credentials.provider.CreateEntry
 import androidx.credentials.provider.RemoteEntry
@@ -177,9 +177,9 @@
                         "androidx.credentials.BUNDLE_KEY_TYPE_PRIORITY_VALUE",
                         when (option.type) {
                             PasswordCredential.TYPE_PASSWORD_CREDENTIAL ->
-                                PriorityHints.PRIORITY_PASSWORD_OR_SIMILAR
+                                CredentialOption.PRIORITY_PASSWORD_OR_SIMILAR
                             PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL -> 100
-                            else -> PriorityHints.PRIORITY_DEFAULT
+                            else -> CredentialOption.PRIORITY_DEFAULT
                         }
                 )
                 typePriorityMap[option.type] = priority
@@ -349,8 +349,8 @@
                 }
                 is CreateCustomCredentialRequest -> {
                     // TODO: directly use the display info once made public
-                    val displayInfo = CreateCredentialRequest.DisplayInfo
-                        .parseFromCredentialDataBundle(createCredentialRequest.credentialData)
+                    val displayInfo = CreateCredentialRequest.DisplayInfo.createFrom(
+                            createCredentialRequest.credentialData)
                         ?: return null
                     RequestDisplayInfo(
                         title = displayInfo.userId.toString(),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 0da32bd..0b40d11 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -71,8 +71,6 @@
 import java.util.ArrayList
 import java.util.Objects
 import java.util.concurrent.Executors
-import org.json.JSONException
-import org.json.JSONObject
 
 class CredentialAutofillService : AutofillService() {
 
@@ -81,13 +79,6 @@
 
         private const val SESSION_ID_KEY = "autofill_session_id"
         private const val REQUEST_ID_KEY = "autofill_request_id"
-        private const val CRED_HINT_PREFIX = "credential="
-        private const val REQUEST_DATA_KEY = "requestData"
-        private const val CANDIDATE_DATA_KEY = "candidateQueryData"
-        private const val SYS_PROVIDER_REQ_KEY = "isSystemProviderRequired"
-        private const val CRED_OPTIONS_KEY = "credentialOptions"
-        private const val TYPE_KEY = "type"
-        private const val REQ_TYPE_KEY = "get"
     }
 
     override fun onFillRequest(
@@ -740,7 +731,6 @@
             uniqueAutofillIdsForRequest: MutableSet<AutofillId>
     ) {
         val traversedViewNodes: MutableSet<AutofillId> = mutableSetOf()
-        val credentialOptionsFromHints: MutableMap<String, CredentialOption> = mutableMapOf()
         val windowNodes: List<AssistStructure.WindowNode> =
                 structure.run {
                     (0 until windowNodeCount).map { getWindowNodeAt(it) }
@@ -749,7 +739,7 @@
         windowNodes.forEach { windowNode: AssistStructure.WindowNode ->
             traverseNodeForRequest(
                 windowNode.rootViewNode, cmRequests, responseClientState, traversedViewNodes,
-                credentialOptionsFromHints, sessionId, uniqueAutofillIdsForRequest)
+                sessionId, uniqueAutofillIdsForRequest)
         }
     }
 
@@ -758,7 +748,6 @@
             cmRequests: MutableList<CredentialOption>,
             responseClientState: Bundle,
             traversedViewNodes: MutableSet<AutofillId>,
-            credentialOptionsFromHints: MutableMap<String, CredentialOption>,
             sessionId: Int,
             uniqueAutofillIdsForRequest: MutableSet<AutofillId>
     ) {
@@ -769,9 +758,8 @@
                 responseClientState.putBoolean(
                     WEBVIEW_REQUESTED_CREDENTIAL_KEY, true)
             }
-            cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, it, responseClientState,
-                traversedViewNodes, credentialOptionsFromHints, sessionId,
-                uniqueAutofillIdsForRequest)
+            cmRequests.addAll(getCredentialOptionsFromViewNode(viewNode, traversedViewNodes,
+                    sessionId, uniqueAutofillIdsForRequest)
             )
             traversedViewNodes.add(it)
         }
@@ -783,18 +771,15 @@
 
         children.forEach { childNode: AssistStructure.ViewNode ->
             traverseNodeForRequest(
-                childNode, cmRequests, responseClientState, traversedViewNodes,
-                credentialOptionsFromHints, sessionId, uniqueAutofillIdsForRequest
+                childNode, cmRequests, responseClientState, traversedViewNodes, sessionId,
+                    uniqueAutofillIdsForRequest
             )
         }
     }
 
     private fun getCredentialOptionsFromViewNode(
             viewNode: AssistStructure.ViewNode,
-            autofillId: AutofillId,
-            responseClientState: Bundle,
             traversedViewNodes: MutableSet<AutofillId>,
-            credentialOptionsFromHints: MutableMap<String, CredentialOption>,
             sessionId: Int,
             uniqueAutofillIdsForRequest: MutableSet<AutofillId>
     ): MutableList<CredentialOption> {
@@ -830,85 +815,6 @@
                         }
                 }
         }
-        // TODO(b/325502552): clean up cred option logic in autofill hint
-        val credentialHints: MutableList<String> = mutableListOf()
-
-        if (viewNode.autofillHints != null) {
-            for (hint in viewNode.autofillHints!!) {
-                if (hint.startsWith(CRED_HINT_PREFIX)) {
-                    credentialHints.add(hint.substringAfter(CRED_HINT_PREFIX))
-                    if (viewNode.webDomain != null) {
-                        responseClientState.putBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, true)
-                    }
-                }
-            }
-        }
-
-        for (credentialHint in credentialHints) {
-            try {
-                convertJsonToCredentialOption(
-                    credentialHint, autofillId, credentialOptionsFromHints)
-                        .let { credentialOptions.addAll(it) }
-            } catch (e: JSONException) {
-                Log.i(TAG, "Exception while parsing response: " + e.message)
-            }
-        }
         return credentialOptions
     }
-
-    private fun convertJsonToCredentialOption(
-        jsonString: String,
-        autofillId: AutofillId,
-        credentialOptionsFromHints: MutableMap<String, CredentialOption>
-    ): List<CredentialOption> {
-        val credentialOptions: MutableList<CredentialOption> = mutableListOf()
-
-        val json = JSONObject(jsonString)
-        val jsonGet = json.getJSONObject(REQ_TYPE_KEY)
-        val options = jsonGet.getJSONArray(CRED_OPTIONS_KEY)
-        for (i in 0 until options.length()) {
-            val option = options.getJSONObject(i)
-            val optionString = option.toString()
-            credentialOptionsFromHints[optionString]
-                    ?.let { credentialOption ->
-                        // if the current credential option was seen before, add the current
-                        // viewNode to the credential option, but do not add it to the option list
-                        // again. This will result in the same result as deduping based on
-                        // traversed viewNode.
-                        credentialOption.candidateQueryData.getParcelableArrayList(
-                            CredentialProviderService.EXTRA_AUTOFILL_ID, AutofillId::class.java)
-                                ?.let {
-                                    it.add(autofillId)
-                                    credentialOption.candidateQueryData.putParcelableArrayList(
-                                        CredentialProviderService.EXTRA_AUTOFILL_ID, it)
-                                }
-            } ?: run {
-                val candidateBundle = convertJsonToBundle(option.getJSONObject(CANDIDATE_DATA_KEY))
-                candidateBundle.putParcelableArrayList(
-                    CredentialProviderService.EXTRA_AUTOFILL_ID,
-                    arrayListOf(autofillId))
-                val credentialOption = CredentialOption(
-                    option.getString(TYPE_KEY),
-                    convertJsonToBundle(option.getJSONObject(REQUEST_DATA_KEY)),
-                    candidateBundle,
-                    option.getBoolean(SYS_PROVIDER_REQ_KEY),
-                )
-                credentialOptions.add(credentialOption)
-                credentialOptionsFromHints[optionString] = credentialOption
-            }
-        }
-        return credentialOptions
-    }
-
-    private fun convertJsonToBundle(json: JSONObject): Bundle {
-        val result = Bundle()
-        json.keys().forEach {
-            val v = json.get(it)
-            when (v) {
-                is String -> result.putString(it, v)
-                is Boolean -> result.putBoolean(it, v)
-            }
-        }
-        return result
-    }
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
index c35721c..772e02e 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -34,6 +34,7 @@
 import com.android.credentialmanager.getflow.RequestDisplayInfo
 import com.android.credentialmanager.getflow.generateDisplayTitleTextResCode
 import com.android.credentialmanager.model.BiometricRequestInfo
+import com.android.credentialmanager.model.CredentialType
 import com.android.credentialmanager.model.EntryInfo
 import com.android.credentialmanager.model.creation.CreateOptionInfo
 import com.android.credentialmanager.model.get.CredentialEntryInfo
@@ -476,7 +477,9 @@
         return null
     }
     val singleEntryType = selectedEntry.credentialType
-    val username = selectedEntry.userName
+    val descriptionName = if (singleEntryType == CredentialType.PASSKEY &&
+        !selectedEntry.displayName.isNullOrBlank()) selectedEntry.displayName else
+        selectedEntry.userName
 
     // TODO(b/336362538) : In W, utilize updated localization strings
     displayTitleText = context.getString(
@@ -487,7 +490,7 @@
     descriptionText = context.getString(
         R.string.get_dialog_description_single_tap,
         getRequestDisplayInfo.appName,
-        username
+        descriptionName
     )
 
     return BiometricDisplayInfo(providerIcon = icon, providerName = providerName,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
index f65a1b7..c48e7e4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/BottomSheet.kt
@@ -71,7 +71,7 @@
                 },
                 scrimColor = MaterialTheme.colorScheme.scrim.copy(alpha = .32f),
                 shape = EntryShape.TopRoundedCorner,
-                windowInsets = WindowInsets.navigationBars,
+                contentWindowInsets = { WindowInsets.navigationBars },
                 dragHandle = null,
                 // Never take over the full screen. We always want to leave some top scrim space
                 // for exiting and viewing the underlying app to help a user gain context.
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 19f5a99..314cc05 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -19,7 +19,7 @@
 import android.credentials.flags.Flags.selectorUiImprovementsEnabled
 import android.credentials.flags.Flags.credmanBiometricApiEnabled
 import android.graphics.drawable.Drawable
-import androidx.credentials.PriorityHints
+import androidx.credentials.CredentialOption
 import com.android.credentialmanager.R
 import com.android.credentialmanager.model.CredentialType
 import com.android.credentialmanager.model.get.ProviderInfo
@@ -322,10 +322,10 @@
         // First rank by priorities of each credential type.
         if (p0.rawCredentialType != p1.rawCredentialType) {
             val p0Priority = typePriorityMap.getOrDefault(
-                    p0.rawCredentialType, PriorityHints.PRIORITY_DEFAULT
+                    p0.rawCredentialType, CredentialOption.PRIORITY_DEFAULT
             )
             val p1Priority = typePriorityMap.getOrDefault(
-                    p1.rawCredentialType, PriorityHints.PRIORITY_DEFAULT
+                    p1.rawCredentialType, CredentialOption.PRIORITY_DEFAULT
             )
             if (p0Priority < p1Priority) {
                 return -1
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 652e62c..35addb3 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -18,12 +18,12 @@
 
 import android.content.Intent
 import android.os.Bundle
+import android.util.Log
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.activity.viewModels
 import com.android.credentialmanager.ui.theme.WearCredentialSelectorTheme
 import com.android.credentialmanager.ui.WearApp
-import com.google.android.horologist.annotations.ExperimentalHorologistApi
 import dagger.hilt.android.AndroidEntryPoint
 
 @AndroidEntryPoint(ComponentActivity::class)
@@ -31,8 +31,8 @@
 
     private val viewModel: CredentialSelectorViewModel by viewModels()
 
-    @OptIn(ExperimentalHorologistApi::class)
     override fun onCreate(savedInstanceState: Bundle?) {
+        Log.d(TAG, "onCreate, intent: $intent")
         super.onCreate(savedInstanceState)
         setTheme(android.R.style.Theme_DeviceDefault)
         setContent {
@@ -47,6 +47,7 @@
     }
 
     override fun onNewIntent(intent: Intent) {
+        Log.d(TAG, "onNewIntent, intent: $intent")
         super.onNewIntent(intent)
         setIntent(intent)
         viewModel.updateRequest(intent)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
index c641d7f..25bc381 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
@@ -15,6 +15,7 @@
  */
 package com.android.credentialmanager.ui.components
 
+import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme
 import androidx.compose.foundation.layout.Row
 import androidx.compose.material3.Icon
 import android.graphics.drawable.Drawable
@@ -22,7 +23,11 @@
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Lock
+import androidx.compose.material.icons.outlined.LockOpen
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
@@ -46,7 +51,7 @@
     onClick: () -> Unit,
     secondaryLabel: String? = null,
     icon: Drawable? = null,
-    isAuthenticationEntryLocked: Boolean = false,
+    isAuthenticationEntryLocked: Boolean? = null,
     textAlign: TextAlign = TextAlign.Center,
     modifier: Modifier = Modifier,
     colors: ChipColors = ChipDefaults.secondaryChipColors()
@@ -77,7 +82,7 @@
     text: @Composable () -> Unit,
     secondaryLabel: String? = null,
     icon: Drawable? = null,
-    isAuthenticationEntryLocked: Boolean = false,
+    isAuthenticationEntryLocked: Boolean? = null,
     modifier: Modifier = Modifier,
     colors: ChipColors = ChipDefaults.primaryChipColors(),
     ) {
@@ -94,16 +99,23 @@
                         text = secondaryLabel,
                     )
 
-                    if (isAuthenticationEntryLocked)
-                    // TODO(b/324465527) change this to lock icon and correct size once figma mocks are
-                    // updated
-                        Icon(
-                            bitmap = checkNotNull(icon?.toBitmap()?.asImageBitmap()),
-                            // Decorative purpose only.
-                            contentDescription = null,
-                            modifier = Modifier.size(10.dp),
-                            tint = Color.Unspecified
-                        )
+                    if (isAuthenticationEntryLocked != null) {
+                        if (isAuthenticationEntryLocked) {
+                            Icon(
+                                Icons.Outlined.Lock,
+                                contentDescription = null,
+                                modifier = Modifier.size(12.dp).align(Alignment.CenterVertically),
+                                tint = WearMaterialTheme.colors.onSurfaceVariant
+                            )
+                        } else {
+                            Icon(
+                                Icons.Outlined.LockOpen,
+                                contentDescription = null,
+                                modifier = Modifier.size(12.dp).align(Alignment.CenterVertically),
+                                tint = WearMaterialTheme.colors.onSurfaceVariant
+                            )
+                        }
+                    }
                 }
             }
         }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
index 22f6bf0..282fea0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
@@ -22,7 +22,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
@@ -57,7 +56,6 @@
     text: String,
     textAlign: TextAlign = TextAlign.Center,
     modifier: Modifier = Modifier,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
 ) {
     Text(
         modifier = modifier.padding(start = 8.dp, end = 8.dp).wrapContentSize(),
@@ -67,7 +65,6 @@
         overflow = TextOverflow.Ellipsis,
         textAlign = textAlign,
         maxLines = 2,
-        onTextLayout = onTextLayout,
     )
 }
 
@@ -79,7 +76,6 @@
     maxLines: Int = 1,
     modifier: Modifier = Modifier,
     color: Color = WearMaterialTheme.colors.onSurface,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
 ) {
     Text(
         modifier = modifier.wrapContentSize(),
@@ -89,7 +85,6 @@
         overflow = TextOverflow.Ellipsis,
         textAlign = textAlign,
         maxLines = maxLines,
-        onTextLayout = onTextLayout,
     )
 }
 
@@ -97,7 +92,6 @@
 fun WearSecondaryLabel(
     text: String,
     modifier: Modifier = Modifier,
-    onTextLayout: (TextLayoutResult) -> Unit = {},
 ) {
     Text(
         modifier = modifier.wrapContentSize(),
@@ -107,6 +101,5 @@
         overflow = TextOverflow.Ellipsis,
         textAlign = TextAlign.Start,
         maxLines = 1,
-        onTextLayout = onTextLayout,
     )
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index 473094c..2656275 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -43,7 +43,8 @@
 
             var icon: Drawable? = null
             // provide icon if all entries have the same provider
-            if (sortedEntries.all {it.providerId == sortedEntries[0].providerId}) {
+            if (sortedEntries.isNotEmpty() &&
+                sortedEntries.all {it.providerId == sortedEntries[0].providerId}) {
                 icon = providerInfos[0].icon
             }
 
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
index fb81e73..36e9792 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
@@ -75,7 +75,10 @@
                     CredentialsScreenChip(
                         label = credential.userName,
                         onClick = { selectEntry(credential, false) },
-                        secondaryLabel = credential.credentialTypeDisplayName,
+                        secondaryLabel =
+                        credential.credentialTypeDisplayName.ifEmpty {
+                            credential.providerDisplayName
+                         },
                         icon = credential.icon,
                         textAlign = TextAlign.Start
                     )
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
index 7addc74..ce2bad0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
@@ -61,13 +61,12 @@
         val credentials = credentialSelectorUiState.sortedEntries
         item {
             var title = stringResource(R.string.choose_sign_in_title)
-
-            if (credentials.isEmpty()) {
-                title = stringResource(R.string.choose_sign_in_title)
-            } else if (credentials.all{ it.credentialType == CredentialType.PASSKEY }) {
-                title = stringResource(R.string.choose_passkey_title)
-            } else if (credentials.all { it.credentialType == CredentialType.PASSWORD }) {
-                title = stringResource(R.string.choose_password_title)
+            if (credentials.isNotEmpty()) {
+                if (credentials.all { it.credentialType == CredentialType.PASSKEY }) {
+                    title = stringResource(R.string.choose_passkey_title)
+                } else if (credentials.all { it.credentialType == CredentialType.PASSWORD }) {
+                    title = stringResource(R.string.choose_password_title)
+                }
             }
 
             SignInHeader(
@@ -77,16 +76,19 @@
         }
 
         credentials.forEach { credential: CredentialEntryInfo ->
-            item {
-                CredentialsScreenChip(
-                    label = credential.userName,
-                    onClick = { selectEntry(credential, false) },
-                    secondaryLabel = credential.credentialTypeDisplayName,
-                    icon = credential.icon,
-                )
-                CredentialsScreenChipSpacer()
+                item {
+                    CredentialsScreenChip(
+                        label = credential.userName,
+                        onClick = { selectEntry(credential, false) },
+                        secondaryLabel =
+                        credential.credentialTypeDisplayName.ifEmpty {
+                            credential.providerDisplayName
+                        },
+                        icon = credential.icon,
+                    )
+                    CredentialsScreenChipSpacer()
+                }
             }
-        }
 
         credentialSelectorUiState.authenticationEntryList.forEach { authenticationEntryInfo ->
             item {
@@ -96,7 +98,6 @@
                 CredentialsScreenChipSpacer()
             }
         }
-
         item {
             Spacer(modifier = Modifier.size(8.dp))
         }
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 38a6d13..5ab190d 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index bcd9836..51a8c46 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 38a6d13..5ab190d 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index f778904..fcd0273 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index 1500583..754abb2 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -33,7 +33,7 @@
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
 
     <application
-        android:icon="@drawable/android14_patch_adaptive"
+        android:icon="@drawable/android15_patch_adaptive"
         android:label="@string/app_name">
 
         <!-- Android V easter egg: Daydream version of Landroid
@@ -41,7 +41,7 @@
         <service
             android:name=".landroid.DreamUniverse"
             android:exported="true"
-            android:icon="@drawable/android14_patch_adaptive"
+            android:icon="@drawable/android15_patch_adaptive"
             android:label="@string/v_egg_name"
             android:description="@string/dream_description"
             android:enabled="false"
@@ -62,7 +62,7 @@
             android:name=".landroid.MainActivity"
             android:exported="true"
             android:label="@string/u_egg_name"
-            android:icon="@drawable/android14_patch_adaptive"
+            android:icon="@drawable/android15_patch_adaptive"
             android:configChanges="orientation|screenLayout|screenSize|density"
             android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen">
             <intent-filter>
diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml
new file mode 100644
index 0000000..d949200
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive.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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/android15_patch_adaptive_background"/>
+    <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/>
+    <monochrome android:drawable="@drawable/android15_patch_monochrome"/>
+</adaptive-icon>
diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml
new file mode 100644
index 0000000..642b30a
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <!-- space -->
+    <path
+        android:pathData="M0,0h108v108h-108z"
+        android:fillColor="#202124"/>
+    <!-- stars -->
+    <group>
+        <path
+            android:pathData="M32,34 h1v1h-1z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M33,61 h1v1h-1z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M71,34 h1v1h-1z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M62,56 h1v1h-1z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M68,47 h1v1h-1z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M72,55 h1v1h-1z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M39,36 h1v1h-1z"
+            android:fillColor="#ffffff"/>
+
+        <path
+            android:pathData="M72,49 h2v2h-2z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M46,53 h2v2h-2z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M32,45 h2v2h-2z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M43,37 h2v2h-2z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M78,51 h2v2h-2z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M34,51 h2v2h-2z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M76,41 h2v2h-2z"
+            android:fillColor="#ffffff"/>
+        <path
+            android:pathData="M39,46 h2v2h-2z"
+            android:fillColor="#ffffff"/>
+  </group>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml
new file mode 100644
index 0000000..1100eb7
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml
@@ -0,0 +1,60 @@
+<?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="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+
+    <!-- zoomies -->
+    <group>
+        <path
+            android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z"
+            android:fillColor="#C6FF00"
+            android:fillType="evenOdd"/>
+        <path
+            android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z"
+            android:fillColor="#ffffff"
+            android:fillType="evenOdd"/>
+        <path
+            android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+            android:fillColor="#ffffff"/>
+    </group>
+    <group>
+        <!-- head! it's like sputnik -->
+        <path
+            android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0"
+            android:fillColor="#34A853"/>
+        <!-- ant -->
+        <path
+            android:pathData="M38,63.5L44.5,74.5"
+            android:strokeWidth="5"
+            android:fillColor="#00000000"
+            android:strokeColor="#34A853"
+            android:strokeLineCap="round"/>
+        <path
+            android:pathData="M70,63.5L63.5,74.5"
+            android:strokeWidth="5"
+            android:fillColor="#00000000"
+            android:strokeColor="#34A853"
+            android:strokeLineCap="round"/>
+    </group>
+    <!-- spaceship -->
+    <path
+        android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z"
+        android:fillColor="#E9F3EB"/>
+</vector>
diff --git a/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml
new file mode 100644
index 0000000..a91cc86
--- /dev/null
+++ b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml
@@ -0,0 +1,119 @@
+<?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="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+  <group>
+    <path
+        android:pathData="
+        M54,94.25
+        m-26.25,0
+        a26.25,26.25 0,1 1,52.5 0
+        a26.25,26.25 0,1 1,-52.5 0
+        "
+        android:fillColor="#000000"/>
+    <path
+        android:pathData="M38,63.5L44.5,74.5"
+        android:strokeWidth="5"
+        android:fillColor="#00000000"
+        android:strokeColor="#000000"
+        android:strokeLineCap="round"/>
+    <path
+        android:pathData="M70,63.5L63.5,74.5"
+        android:strokeWidth="5"
+        android:fillColor="#00000000"
+        android:strokeColor="#000000"
+        android:strokeLineCap="round"/>
+
+    <path
+        android:pathData="
+        M54,34
+        C52.34,34 51,35.29 51,36.88
+        V40.44
+        C51,40.75 51.25,41 51.56,41
+        C51.87,41 52.13,40.75 52.13,40.44
+        V39.48
+        C52.13,38.87 52.63,38.37 53.25,38.37
+        H54.75
+        C55.37,38.37 55.87,38.87 55.87,39.48
+        V40.44
+        C55.87,40.75 56.13,41 56.44,41
+        C56.75,41 57,40.75 57,40.44
+        V36.88
+        C57,35.29 55.66,34 54,34
+        H54
+        Z
+        "
+        android:fillColor="#34A853"/>
+    <path
+        android:strokeWidth="1"
+        android:pathData="M54,40V67"
+        android:fillColor="#00000000"
+        android:strokeColor="#40FFFFFF"
+        />
+
+    <path
+        android:pathData="M32,34 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M33,61 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M71,34 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M62,56 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M68,47 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M72,55 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M39,36 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+
+    <path
+        android:pathData="M72,49 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M46,53 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M32,45 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M43,37 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M78,51 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M34,51 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M76,41 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M39,46 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+
+  </group>
+</vector>
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Autopilot.kt b/packages/EasterEgg/src/com/android/egg/landroid/Autopilot.kt
index f71abee..fb5954e 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/Autopilot.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/Autopilot.kt
@@ -21,7 +21,8 @@
 
 class Autopilot(val ship: Spacecraft, val universe: Universe) : Entity {
     val BRAKING_TIME = 5f
-    val SIGHTSEEING_TIME = 10f
+    val SIGHTSEEING_TIME = 15f
+    val LAUNCH_THRUST_TIME = 5f
     val STRATEGY_MIN_TIME = 0.5f
 
     var enabled = false
@@ -73,7 +74,7 @@
 
                 strategy = "LAUNCHING"
                 debug = ""
-                nextStrategyTime = sim.now + 2f
+                nextStrategyTime = sim.now + LAUNCH_THRUST_TIME
             }
         } else {
             // select new target
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt b/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt
index 79f8b5fc..16ec1a9 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/MainActivity.kt
@@ -17,6 +17,7 @@
 package com.android.egg.landroid
 
 import android.content.res.Resources
+import android.os.Build
 import android.os.Bundle
 import android.util.Log
 import androidx.activity.ComponentActivity
@@ -119,6 +120,26 @@
     }.absoluteValue
 }
 
+fun getDessertCode(): String =
+    when (Build.VERSION.SDK_INT) {
+        Build.VERSION_CODES.LOLLIPOP -> "LMP"
+        Build.VERSION_CODES.LOLLIPOP_MR1 -> "LM1"
+        Build.VERSION_CODES.M -> "MNC"
+        Build.VERSION_CODES.N -> "NYC"
+        Build.VERSION_CODES.N_MR1 -> "NM1"
+        Build.VERSION_CODES.O -> "OC"
+        Build.VERSION_CODES.P -> "PIE"
+        Build.VERSION_CODES.Q -> "QT"
+        Build.VERSION_CODES.R -> "RVC"
+        Build.VERSION_CODES.S -> "SC"
+        Build.VERSION_CODES.S_V2 -> "SC2"
+        Build.VERSION_CODES.TIRAMISU -> "TM"
+        Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> "UDC"
+        Build.VERSION_CODES.VANILLA_ICE_CREAM -> "VIC"
+        else -> Build.VERSION.RELEASE_OR_CODENAME.replace(Regex("[a-z]*"), "")
+    }
+
+
 val DEBUG_TEXT = mutableStateOf("Hello Universe")
 const val SHOW_DEBUG_TEXT = false
 
@@ -239,7 +260,8 @@
                 text =
                     (with(universe.star) {
                             listOf(
-                                "  STAR: $name (UDC-${universe.randomSeed % 100_000})",
+                                "  STAR: $name (${getDessertCode()}-" +
+                                    "${universe.randomSeed % 100_000})",
                                 " CLASS: ${cls.name}",
                                 "RADIUS: ${radius.toInt()}",
                                 "  MASS: %.3g".format(mass),
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
index fc66ad6..d14234e 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/Physics.kt
@@ -20,7 +20,7 @@
 import kotlin.random.Random
 
 // artificially speed up or slow down the simulation
-const val TIME_SCALE = 1f
+const val TIME_SCALE = 1f // simulation seconds per wall clock second
 
 // if it's been over 1 real second since our last timestep, don't simulate that elapsed time.
 // this allows the simulation to "pause" when, for example, the activity pauses
@@ -36,6 +36,19 @@
     fun postUpdate(sim: Simulator, dt: Float)
 }
 
+interface Removable {
+    fun canBeRemoved(): Boolean
+}
+
+class Fuse(var lifetime: Float) : Removable {
+    fun update(dt: Float) {
+        lifetime -= dt
+    }
+    override fun canBeRemoved(): Boolean {
+        return lifetime < 0
+    }
+}
+
 open class Body(var name: String = "Unknown") : Entity {
     var pos = Vec2.Zero
     var opos = Vec2.Zero
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
index 1e54569..30de11b1 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/Universe.kt
@@ -43,11 +43,9 @@
 const val MAIN_ENGINE_ACCEL = 1000f // thrust effect, pixels per second squared
 const val LAUNCH_MECO = 2f // how long to suspend gravity when launching
 
-const val SCALED_THRUST = true
+const val LANDING_REMOVAL_TIME = 60 * 15f // 15 min of simulation time
 
-interface Removable {
-    fun canBeRemoved(): Boolean
-}
+const val SCALED_THRUST = true
 
 open class Planet(
     val orbitCenter: Vec2,
@@ -321,7 +319,7 @@
                         //
                         (1..10).forEach {
                             Spark(
-                                    lifetime = rng.nextFloatInRange(0.5f, 2f),
+                                    ttl = rng.nextFloatInRange(0.5f, 2f),
                                     style = Spark.Style.DOT,
                                     color = Color.White,
                                     size = 1f
@@ -359,13 +357,22 @@
         entities
             .filterIsInstance<Removable>()
             .filter(predicate = Removable::canBeRemoved)
-            .filterIsInstance<Entity>()
-            .forEach { remove(it) }
+            .forEach { remove(it as Entity) }
+
+        constraints
+            .filterIsInstance<Removable>()
+            .filter(predicate = Removable::canBeRemoved)
+            .forEach { remove(it as Constraint) }
     }
 }
 
-class Landing(var ship: Spacecraft?, val planet: Planet, val angle: Float, val text: String = "") :
-    Constraint {
+class Landing(
+    var ship: Spacecraft?,
+    val planet: Planet,
+    val angle: Float,
+    val text: String = "",
+    private val fuse: Fuse = Fuse(LANDING_REMOVAL_TIME)
+) : Constraint, Removable by fuse {
     override fun solve(sim: Simulator, dt: Float) {
         ship?.let { ship ->
             val landingVector = Vec2.makeWithAngleMag(angle, ship.radius + planet.radius)
@@ -373,17 +380,20 @@
             ship.pos = (ship.pos * 0.5f) + (desiredPos * 0.5f) // @@@ FIXME
             ship.angle = angle
         }
+
+        fuse.update(dt)
     }
 }
 
 class Spark(
-    var lifetime: Float,
+    var ttl: Float,
     collides: Boolean = false,
     mass: Float = 0f,
     val style: Style = Style.LINE,
     val color: Color = Color.Gray,
-    val size: Float = 2f
-) : Removable, Body() {
+    val size: Float = 2f,
+    val fuse: Fuse = Fuse(ttl)
+) : Removable by fuse, Body(name = "Spark") {
     enum class Style {
         LINE,
         LINE_ABSOLUTE,
@@ -398,10 +408,7 @@
     }
     override fun update(sim: Simulator, dt: Float) {
         super.update(sim, dt)
-        lifetime -= dt
-    }
-    override fun canBeRemoved(): Boolean {
-        return lifetime < 0
+        fuse.update(dt)
     }
 }
 
@@ -486,11 +493,11 @@
             // exhaust
             sim.add(
                 Spark(
-                        lifetime = sim.rng.nextFloatInRange(0.5f, 1f),
+                        ttl = sim.rng.nextFloatInRange(0.5f, 1f),
                         collides = true,
                         mass = 1f,
                         style = Spark.Style.RING,
-                        size = 3f,
+                        size = 1f,
                         color = Color(0x40FFFFFF)
                     )
                     .also { spark ->
diff --git a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
index 974784d..ed3ebc7 100644
--- a/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
+++ b/packages/EasterEgg/src/com/android/egg/landroid/VisibleUniverse.kt
@@ -30,6 +30,7 @@
 import androidx.core.math.MathUtils.clamp
 import com.android.egg.flags.Flags.flagFlag
 import java.lang.Float.max
+import kotlin.math.exp
 import kotlin.math.sqrt
 
 const val DRAW_ORBITS = true
@@ -289,7 +290,8 @@
 
 fun ZoomedDrawScope.drawSpark(spark: Spark) {
     with(spark) {
-        if (lifetime < 0) return
+        if (fuse.lifetime < 0) return
+        val life = 1f - fuse.lifetime / ttl
         when (style) {
             Spark.Style.LINE ->
                 if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size)
@@ -297,7 +299,13 @@
                 if (opos != Vec2.Zero) drawLine(color, opos, pos, strokeWidth = size / zoom)
             Spark.Style.DOT -> drawCircle(color, size, pos)
             Spark.Style.DOT_ABSOLUTE -> drawCircle(color, size, pos / zoom)
-            Spark.Style.RING -> drawCircle(color, size, pos, style = Stroke(width = 1f / zoom))
+            Spark.Style.RING ->
+                drawCircle(
+                    color = color.copy(alpha = color.alpha * (1f - life)),
+                    radius = exp(lerp(size, 3f * size, life)) - 1f,
+                    center = pos,
+                    style = Stroke(width = 1f / zoom)
+                )
         }
     }
 }
diff --git a/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm
new file mode 100644
index 0000000..6fa54f9
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_cyrillic.kcm
@@ -0,0 +1,320 @@
+# 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.
+
+#
+# Serbian and Montenegrin (Cyrillic) keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+    label:                              '`'
+    base:                               '`'
+    shift:                              '~'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '\u0022'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '&'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '/'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '('
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              ')'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              '='
+}
+
+key MINUS {
+    label:                              '\''
+    base:                               '\u030d'
+    shift:                              '?'
+}
+
+key EQUALS {
+    label:                              '+'
+    base:                               '+'
+    shift:                              '*'
+}
+
+### ROW 2
+
+key Q {
+    label:                              '\u0409'
+    base, capslock+shift:               '\u0459'
+    shift, capslock:                    '\u0409'
+}
+
+key W {
+    label:                              '\u040a'
+    base, capslock+shift:               '\u045a'
+    shift, capslock:                    '\u040a'
+}
+
+key E {
+    label:                              '\u0415'
+    base, capslock+shift:               '\u0435'
+    shift, capslock:                    '\u0415'
+    ralt:                               '\u20ac'
+}
+
+key R {
+    label:                              '\u0420'
+    base, capslock+shift:               '\u0440'
+    shift, capslock:                    '\u0420'
+}
+
+key T {
+    label:                              '\u0422'
+    base, capslock+shift:               '\u0442'
+    shift, capslock:                    '\u0422'
+}
+
+key Y {
+    label:                              '\u0417'
+    base, capslock+shift:               '\u0437'
+    shift, capslock:                    '\u0417'
+}
+
+key U {
+    label:                              '\u0423'
+    base, capslock+shift:               '\u0443'
+    shift, capslock:                    '\u0423'
+}
+
+key I {
+    label:                              '\u0418'
+    base, capslock+shift:               '\u0438'
+    shift, capslock:                    '\u0418'
+}
+
+key O {
+    label:                              '\u041e'
+    base, capslock+shift:               '\u043e'
+    shift, capslock:                    '\u041e'
+}
+
+key P {
+    label:                              '\u041f'
+    base, capslock+shift:               '\u043f'
+    shift, capslock:                    '\u041f'
+}
+
+key LEFT_BRACKET {
+    label:                              '\u0428'
+    base, capslock+shift:               '\u0448'
+    shift, capslock:                    '\u0428'
+}
+
+key RIGHT_BRACKET {
+    label:                              '\u0402'
+    base, capslock+shift:               '\u0452'
+    shift, capslock:                    '\u0402'
+}
+
+### ROW 3
+
+key A {
+    label:                              '\u0410'
+    base, capslock+shift:               '\u0430'
+    shift, capslock:                    '\u0410'
+}
+
+key S {
+    label:                              '\u0421'
+    base, capslock+shift:               '\u0441'
+    shift, capslock:                    '\u0421'
+}
+
+key D {
+    label:                              '\u0414'
+    base, capslock+shift:               '\u0434'
+    shift, capslock:                    '\u0414'
+}
+
+key F {
+    label:                              '\u0424'
+    base, capslock+shift:               '\u0444'
+    shift, capslock:                    '\u0424'
+}
+
+key G {
+    label:                              '\u0413'
+    base, capslock+shift:               '\u0433'
+    shift, capslock:                    '\u0413'
+}
+
+key H {
+    label:                              '\u0425'
+    base, capslock+shift:               '\u0445'
+    shift, capslock:                    '\u0425'
+}
+
+key J {
+    label:                              '\u0408'
+    base, capslock+shift:               '\u0458'
+    shift, capslock:                    '\u0408'
+}
+
+key K {
+    label:                              '\u041a'
+    base, capslock+shift:               '\u043a'
+    shift, capslock:                    '\u041a'
+}
+
+key L {
+    label:                              '\u041b'
+    base, capslock+shift:               '\u043b'
+    shift, capslock:                    '\u041b'
+}
+
+key SEMICOLON {
+    label:                              '\u0427'
+    base, capslock+shift:               '\u0447'
+    shift, capslock:                    '\u0427'
+}
+
+key APOSTROPHE {
+    label:                              '\u040b'
+    base, capslock+shift:               '\u045b'
+    shift, capslock:                    '\u040b'
+}
+
+key BACKSLASH {
+    label:                              '\u0416'
+    base, capslock+shift:               '\u0436'
+    shift, capslock:                    '\u0416'
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '<'
+    base:                               '<'
+    shift:                              '>'
+}
+
+key Z {
+    label:                              '\u0405'
+    base, capslock+shift:               '\u0455'
+    shift, capslock:                    '\u0405'
+}
+
+key X {
+    label:                              '\u040f'
+    base, capslock+shift:               '\u045f'
+    shift, capslock:                    '\u040f'
+}
+
+key C {
+    label:                              '\u0426'
+    base, capslock+shift:               '\u0446'
+    shift, capslock:                    '\u0426'
+}
+
+key V {
+    label:                              '\u0412'
+    base, capslock+shift:               '\u0432'
+    shift, capslock:                    '\u0412'
+}
+
+key B {
+    label:                              '\u0411'
+    base, capslock+shift:               '\u0431'
+    shift, capslock:                    '\u0411'
+}
+
+key N {
+    label:                              '\u041d'
+    base, capslock+shift:               '\u043d'
+    shift, capslock:                    '\u041d'
+}
+
+key M {
+    label:                              '\u041c'
+    base, capslock+shift:               '\u043c'
+    shift, capslock:                    '\u041c'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              ';'
+    ralt:                               '<'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              ':'
+    ralt:                               '>'
+}
+
+key SLASH {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+}
\ No newline at end of file
diff --git a/packages/InputDevices/res/raw/keyboard_layout_serbian_latin.kcm b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_latin.kcm
similarity index 99%
rename from packages/InputDevices/res/raw/keyboard_layout_serbian_latin.kcm
rename to packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_latin.kcm
index 0ff1dea..8e4d7b1 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_serbian_latin.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_serbian_and_montenegrin_latin.kcm
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 #
-# Serbian (Latin) keyboard layout.
+# Serbian and Montenegrin (Latin) keyboard layout.
 #
 
 type OVERLAY
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index f7dc457..eafe042 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thais (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serwies (Latyns)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegryns (Latyns)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 712ca00..fd50761 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ታይላንድኛ (ፓታሾት)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ሰርቢያኛ (ላቲን)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ሞንቴኔግሮኛ (ላቲን)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index fe8f59c..ac73038 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"‏التايلاندية (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"الصربية (اللاتينية)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"لغة الجبل الأسود (اللاتينية)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index c2ea39e..1162c72 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"থাই (পাটাচ’টে)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ছাৰ্বিয়ান (লেটিন)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"মণ্টেনেগ্ৰিণ (লেটিন)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index d90c3c8..6a6a99d 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tay (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serb dili (Latın)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Monteneqro dili (Latın)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index 0d9a211..96ac10cb 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srpski (latinica)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"crnogorski (latinica)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index 697ab63..e8341a1 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайская (Патачотэ)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербская (лацініца)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Чарнагорская (лацініца)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index e02fd0c..b6a5b00 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тайландски (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"сръбски (латиница)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"черногорски (латиница)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index 3fdbcc1..353b6dc 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"থাই (পাট্টাচোটে)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"সার্বিয়ান (ল্যাটিন)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"মন্টেনেগ্রিন (ল্যাটিন)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index 934da2c..0145b0a 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajlandski (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srpski (latinica)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"crnogorski (latinica)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index 2082c05..d729c91 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbi (llatí)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrí (llatí)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index cd211c9..170fa58 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thajština (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srbština (latinka)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"černohorština (latinka)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index 9635810..47fcfb1 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbisk (latinsk)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrinsk (latinsk)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 35db12e..1f6b65d 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailändisch (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbisch (lat. Alphabet)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrinisch (lat. Alphabet)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index fb34edd..2a9dd8c 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Ταϊλανδικά (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Σερβικά (Λατινικά)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Μαυροβουνιακά (Λατινικά)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index 0296067..e27fb64 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index 1d7ba3d..3fa4bce 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -54,4 +54,6 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
+    <string name="keyboard_layout_serbian_cyrillic" msgid="7013541044323542196">"Serbian (Cyrillic)"</string>
+    <string name="keyboard_layout_montenegrin_cyrillic" msgid="2391253952894077421">"Montenegrin (Cyrillic)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index 0296067..e27fb64 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index 0296067..e27fb64 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index a231d4c..2ae35ab 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -54,4 +54,6 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎Thai (Pattachote)‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‎Serbian (Latin)‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎Montenegrin (Latin)‎‏‎‎‏‎"</string>
+    <string name="keyboard_layout_serbian_cyrillic" msgid="7013541044323542196">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎‏‏‎‏‎‎‎Serbian (Cyrillic)‎‏‎‎‏‎"</string>
+    <string name="keyboard_layout_montenegrin_cyrillic" msgid="2391253952894077421">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎Montenegrin (Cyrillic)‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index 4d32333..049edf4 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbio (latino)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latino)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 39905de..b6e27c5 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbio (latino)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latino)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index f2d4340..cecf304d 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbia (ladina)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegro (ladina)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 08d96a9b..481b315 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailandiarra (pattachote-a)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbiarra (latindarra)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegroarra (latindarra)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 8dcae86b..9614fef 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"تایلندی (پاتاچوته)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"صربی (لاتین)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"مونته‌نگرویی (لاتین)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index 8748854..afae51b 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thai (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"serbia (latinalainen)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"montenegro (latinalainen)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index ee498aa..f2d79df 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thaï (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbe (latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Monténégrin (latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index f12e1db..61891c3 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thaï (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbe (latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Monténégrin (latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index 2d96c89..4616168 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbio (alfabeto latino)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (alfabeto latino)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index 3c02208..f04b2d2 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"થાઇ (પટ્ટાશોટે)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"સર્બિયન (લેટિન)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"મોંટેનેગ્રીન (લેટિન)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 88729ea1..1017ef1 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पटैचोटे)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"सर्बियन (लैटिन)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"मॉन्टेनीग्रिन (लैटिन)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index 39da355..bc630bc 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srpski (latinica)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"crnogorski (latinica)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index edceda2..5155543 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thai (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"szerb (latin betűs)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"montenegrói (latin betűs)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index 8f3652a..8097438 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"թայերեն (պատաչոտ)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"սերբերեն (լատինատառ)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"չեռնոգորերեն (լատինատառ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index d4f024a..1c52e9c 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbia (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegro (Latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index 680c4e3..bf428ba 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Taílenskt (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbneska (latneskt)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Svartfellska (latneskt)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index b6b9e8d..bb3b12c 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbo (latino)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latino)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 93f7426..f44235a 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"‏תאית (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"סרבית (לטינית)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"מונטנגרית (לטינית)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index bb9fc8c..55b9642 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"タイ語(Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"セルビア語(ラテン文字)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"モンテネグロ語(ラテン)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index e09d65b..8696fb2 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ტაილანდური (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"სერბული (ლათინური)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"მონტენეგრული (ლათინური)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index 176aa24..2e7236c 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тай (паттачот)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Серб (латын жазуы)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Черногор (латын жазуы)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index b8571ec..2689258 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ថៃ (ប៉ាតាឈោត)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ស៊ែប៊ី (ឡាតាំង)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ម៉ុងតេណេហ្គ្រោ (ឡាតាំង)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index 3b0def4..34d9053 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ಥಾಯ್ (ಪಟ್ಟಚೋಟ್)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ಸೆರ್ಬಿಯನ್ (ಲ್ಯಾಟಿನ್)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ಮೊಂಟೆನೆಗ್ರಿನ್ (ಲ್ಯಾಟಿನ್)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 689d3fff..ad99961 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"태국어(Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"세르비아어(로마자)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"몬테네그로어(로마자)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index 97dbc2a..79ff71e 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайча (Pattachote баскычтобу)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербче (Латын)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Монтенегрочо (Латын)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index da2b868..2427d1c 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ໄທ (ປັດຕະໂຊຕິ)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ເຊີບຽນ (ລາຕິນ)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ມອນເທເນກຣິນ (ລາຕິນ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index b9a3e20..48010bc 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tajų („Pattachote“)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbų (lotynų rašmenys)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Juodkalniečių (lotynų rašmenys)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 827480c..a2e8f8f 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Taju (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbu (latīņu)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Melnkalniešu (latīņu)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index 0aef324..13cc8e1 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тајландски (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"српски (латиница)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"црногорски (латиница)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index b8f4d32..fda477b 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"തായ് (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"സെർബിയൻ (ലാറ്റിൻ)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"മോണ്ടിനെഗ്രിൻ (ലാറ്റിൻ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index 2490d81..a6d3320 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тай (паттачоте)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Серби (латин)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Монтенегро (латин)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index 63c4c90..faae021 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पट्टाचोटे)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"सर्बियन (लॅटिन)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"मॉन्टेनेग्रिन (लॅटिन)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 6444ae0..9f14260 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbia (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 0510240..bb26027 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ထိုင်း (ပတ်တာချုတ်)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ဆားဘီးယား (လက်တင်)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"မွန်တီနီဂရင်း (လက်တင်)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 43a172f..402d7ee 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbisk (latinsk)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrisk (latinsk)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index ab1ec1d..a60cf36 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पत्ताचोते)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"सर्बियाली (ल्याटिन)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"मोन्टेनिग्रिन (ल्याटिन)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index 1893704..bb0c945 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Servisch (Latijns)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrijns (Latijns)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index 94fc9b3..2bf862b 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ଥାଇ (ପାଟ୍ଟାଚୋଟେ)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ସର୍ବିଆନ (ଲାଟିନ)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ମଣ୍ଟେନେଗ୍ରିନ (ଲାଟିନ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 970343f..c1fee5f 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ਥਾਈ (ਪੈਟਾਸ਼ੋਟੇ)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ਸਰਬੀਆਈ (ਲਾਤੀਨੀ)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ਮਾਂਟੇਨੀਗਰਿਨ (ਲਾਤੀਨੀ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index b76c0fe..d3e4a3a 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"serbski (alfabet łaciński)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"czarnogórski (alfabet łaciński)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index 18c334f..94a10c5 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sérvio (latim)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latim)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index ea1d9c1..8c22776 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sérvio (latim)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latim)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 18c334f..94a10c5 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sérvio (latim)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latim)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index 9fa3017..654f168 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailandeză (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sârbă (caractere latine)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Muntenegreană (caractere latine)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 90b2461..05b1b25 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайский (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербский (латиница)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Черногорский (латиница)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index 8d42385..8d36553 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"තායි (පට්ටචෝටේ)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"සර්බියානු (ලතින්)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"මොන්ටෙනේග්‍රීන් (ලතින්)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index 64b4ef2..33e1651 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thajčina (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srbčina (latinka)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"čiernohorčina (latinka)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index fdbbca7..e40cca7 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajščina (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"srbščina (latinica)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"črnogorščina (latinica)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index 06a598c..46405ba 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tajlandisht (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbisht (latine)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Malazisht (latine)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index d8beac6..5642040 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тајски (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"српски (латиница)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"црногорски (латиница)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index f1046bd..9ba1227 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thailändska (pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"serbiska (latinskt)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"montenegrinska (latinskt)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 52efd2f..5da8542 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Kitai (Kipatachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Kiserbia (Kilatini)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Kimontenegri (Kilatini)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index 14fd630..becffd3 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"தாய் (பட்டாசொட்டே)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"செர்பியன் (லத்தீன்)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"மாண்டினெக்ரன் (லத்தீன்)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index 891fd6c..1d2fcb4 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"థాయ్ (పత్తచోత్)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"సెర్బియన్ (లాటిన్)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"మాంటెనెగ్రిన్ (లాటిన్)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index b58cc29..318a3bb 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ไทย (ปัตตะโชติ)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"เซอร์เบีย (ละติน)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"มอนเตเนโกร (ละติน)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 3278899..062dff0 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index 6582aaf2..c6d38f4 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tayca (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Sırpça (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Karadağca (Latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 3a544e0..357747b 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайська (паттачоте)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербська (латиниця)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Чорногорська (латиниця)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 4b29326..7b4c104 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"تھائی (پٹاچوٹے)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"سربیائی (لاطینی)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"مونٹے نیگریائی (لاطینی)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 0e80d71..60e7405 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tay (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serb (lotin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Chernogor (lotin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 5094a29..6fe39d2 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tiếng Thái (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Tiếng Serbia (Latinh)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Tiếng Montenegro (Latinh)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index 6a5dd74..6e96e5d 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰语 (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"塞尔维亚语(拉丁字母)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"黑山语(拉丁字母)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index a5a934a..3d1a895 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰文 (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"塞爾維亞文 (拉丁)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"蒙特內哥羅文 (拉丁)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index 54c8b23..26be41b 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰文 (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"塞爾維亞文 (拉丁字母)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"蒙特內哥羅文 (拉丁字母)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 78dfa3b5..f6f6a77 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -54,4 +54,8 @@
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Isi-Thai (Pattachote)"</string>
     <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"IsiSerbian (Latin)"</string>
     <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"IsiMontenegrin (Latin)"</string>
+    <!-- no translation found for keyboard_layout_serbian_cyrillic (7013541044323542196) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_cyrillic (2391253952894077421) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index be7d2c1..5a91125 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -158,4 +158,10 @@
 
     <!-- Montenegrin (Latin) keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_montenegrin_latin">Montenegrin (Latin)</string>
+
+    <!-- Serbian (Cyrillic) keyboard layout label. [CHAR LIMIT=35] -->
+    <string name="keyboard_layout_serbian_cyrillic">Serbian (Cyrillic)</string>
+
+    <!-- Montenegrin (Cyrillic) keyboard layout label. [CHAR LIMIT=35] -->
+    <string name="keyboard_layout_montenegrin_cyrillic">Montenegrin (Cyrillic)</string>
 </resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 84e4b9e..9309489 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -336,14 +336,28 @@
     <keyboard-layout
         android:name="keyboard_layout_serbian_latin"
         android:label="@string/keyboard_layout_serbian_latin"
-        android:keyboardLayout="@raw/keyboard_layout_serbian_latin"
+        android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_latin"
         android:keyboardLocale="sr-Latn-RS"
         android:keyboardLayoutType="qwertz" />
 
     <keyboard-layout
         android:name="keyboard_layout_montenegrin_latin"
         android:label="@string/keyboard_layout_montenegrin_latin"
-        android:keyboardLayout="@raw/keyboard_layout_serbian_latin"
+        android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_latin"
         android:keyboardLocale="cnr-Latn-ME"
         android:keyboardLayoutType="qwertz" />
+
+    <keyboard-layout
+        android:name="keyboard_layout_serbian_cyrillic"
+        android:label="@string/keyboard_layout_serbian_cyrillic"
+        android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic"
+        android:keyboardLocale="sr-Cyrl-RS"
+        android:keyboardLayoutType="extended" />
+
+    <keyboard-layout
+        android:name="keyboard_layout_montenegrin_cyrillic"
+        android:label="@string/keyboard_layout_montenegrin_cyrillic"
+        android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic"
+        android:keyboardLocale="cnr-Cyrl-ME"
+        android:keyboardLayoutType="extended" />
 </keyboard-layouts>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index 1db92a0..80dc889 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -63,7 +63,7 @@
     <string name="archive_application_text_all_users" msgid="3151229641681672580">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
     <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
     <string name="archive_application_text_user" msgid="2586558895535581451">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
-    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਨਿੱਜੀ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਡੀਵਾਈਸ \'ਤੇ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਵੱਲੋਂ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਤੋਂ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index 006ad52..e10eb0e 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -55,7 +55,7 @@
     <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Geçerli kullanıcının bu yüklemeyi kaldırma izni yok."</string>
     <string name="generic_error_dlg_title" msgid="5863195085927067752">"Hata"</string>
     <string name="generic_error_dlg_text" msgid="5287861443265795232">"Uygulamanın yüklemesi kaldırılamadı."</string>
-    <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamanın yüklemesini kaldır"</string>
+    <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamayı kaldır"</string>
     <string name="uninstall_update_title" msgid="824411791011583031">"Güncelleme kaldırılsın mı?"</string>
     <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, şu uygulamanın bir parçasıdır:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index c96644c..03d52d0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
+
 import androidx.annotation.Nullable;
 
 /**
@@ -84,22 +85,28 @@
         int statusCode = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
             PackageInstaller.STATUS_FAILURE);
         boolean returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
-
-        if (returnResult) {
-            int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
+        int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
                 PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
 
+        // TODO (b/346655018): Use INSTALL_FAILED_ABORTED legacyCode in the condition
+        // statusCode can be STATUS_FAILURE_ABORTED if:
+        // 1. GPP blocks an install.
+        // 2. User denies ownership update explicitly.
+        // InstallFailed dialog must not be shown only when the user denies ownership update. We
+        // must show this dialog for all other install failures.
+        boolean userDenied = statusCode == PackageInstaller.STATUS_FAILURE_ABORTED
+                && legacyStatus != PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT
+                && legacyStatus != PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
+
+        if (returnResult) {
             // Return result if requested
             Intent result = new Intent();
             result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
             setResult(Activity.RESULT_FIRST_USER, result);
             finish();
-        } else if (statusCode != PackageInstaller.STATUS_FAILURE_ABORTED) {
-            // statusCode will be STATUS_FAILURE_ABORTED if the update-owner confirmation dialog was
-            // dismissed by the user. We don't want to show a InstallFailed dialog in this case.
-            // If the user denies install permission for normal installs, this dialog will never be
-            // triggered as the status code is returned from PackageInstallerActivity.java
-
+        } else if (userDenied) {
+            finish();
+        } else {
             // Set header icon and title
             PackageUtil.AppSnippet as = intent.getParcelableExtra(
                 PackageInstallerActivity.EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
@@ -127,8 +134,6 @@
 
             // Get status messages
             setExplanationFromErrorCode(statusCode);
-        } else {
-            finish();
         }
     }
 
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/MDnsUtils.java
old mode 100755
new mode 100644
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/ServiceResolver.java
old mode 100755
new mode 100644
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/VendorInfo.java
old mode 100755
new mode 100644
diff --git a/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java b/packages/PrintRecommendationService/src/com/android/printservice/recommendation/plugin/xerox/XeroxPrintServiceRecommendationPlugin.java
old mode 100755
new mode 100644
diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml
index 020eac7..950c5c8 100644
--- a/packages/PrintSpooler/res/values-as/strings.xml
+++ b/packages/PrintSpooler/res/values-as/strings.xml
@@ -100,7 +100,7 @@
   </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"প\'ৰ্ট্ৰেইট"</item>
-    <item msgid="3199660090246166812">"লেণ্ডস্কেইপ"</item>
+    <item msgid="3199660090246166812">"লেণ্ডস্কে’প"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"ফাইলত লিখিব পৰা নহ\'ল"</string>
     <string name="print_error_default_message" msgid="8602678405502922346">"দুঃখিত, প্ৰিণ্টিঙৰ কাম নহ\'ল। পুনৰ চেষ্টা কৰক।"</string>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml
index eb33d57..75e1a18 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml
index eb33d57..75e1a18 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-ta/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-ta/strings.xml
index e7e33d7..766e719 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-ta/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-ta/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"தனிப்பட்டவை"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"பணி"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"தனிப்பட்டவை"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"இரகசியமானவை"</string>
 </resources>
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 45667f5..d6345ce 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-alpha08"
+    extra["jetpackComposeVersion"] = "1.7.0-beta02"
 }
 
 subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 85ad160..a842009 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
 #
 
 [versions]
-agp = "8.3.2"
+agp = "8.5.0"
 compose-compiler = "1.5.11"
 dexmaker-mockito = "2.28.3"
 jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip
deleted file mode 100644
index 7a9ac5a..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.7-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
new file mode 100644
index 0000000..77e6ad3
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.8-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index d64cd49..e644113 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 182095e..91d2a3a 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.7-bin.zip
+distributionUrl=gradle-8.8-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
index 1aa94a4..b740cf1 100755
--- a/packages/SettingsLib/Spa/gradlew
+++ b/packages/SettingsLib/Spa/gradlew
@@ -55,7 +55,7 @@
 #       Darwin, MinGW, and NonStop.
 #
 #   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 #       within the Gradle project.
 #
 #       You can find Gradle at https://github.com/gradle/gradle/.
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 4aa57b3..9b8ecf7 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -53,21 +53,21 @@
 
 dependencies {
     api(project(":SettingsLibColor"))
-    api("androidx.appcompat:appcompat:1.7.0-beta01")
+    api("androidx.appcompat:appcompat:1.7.0-rc01")
     api("androidx.slice:slice-builders:1.1.0-alpha02")
     api("androidx.slice:slice-core:1.1.0-alpha02")
     api("androidx.slice:slice-view:1.1.0-alpha02")
-    api("androidx.compose.material3:material3:1.3.0-alpha06")
+    api("androidx.compose.material3:material3:1.3.0-beta02")
     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.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.8.0-alpha08")
+    api("androidx.navigation:navigation-compose:2.8.0-beta02")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
     api("com.google.android.material:material:1.11.0")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
-    implementation("com.airbnb.android:lottie-compose:5.2.0")
+    implementation("com.airbnb.android:lottie-compose:6.4.0")
 
     androidTestImplementation(project(":testutils"))
     androidTestImplementation(libs.dexmaker.mockito)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
index 3fdb1d1..12d04f9 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
@@ -33,6 +33,9 @@
     // The name of the page provider, who creates this page. It is used to compute the unique id.
     val sppName: String,
 
+    // The category id of the page provider which is the PageId at SettingsEnums.
+    val metricsCategory: Int = 0,
+
     // The display name of the page, for better readability.
     val displayName: String,
 
@@ -46,6 +49,7 @@
         // TODO: cleanup it once all its usage in Settings are switched to Spp.createSettingsPage
         fun create(
             name: String,
+            metricsCategory: Int = 0,
             displayName: String? = null,
             parameter: List<NamedNavArgument> = emptyList(),
             arguments: Bundle? = null
@@ -53,6 +57,7 @@
             return SettingsPage(
                 id = genPageId(name, parameter, arguments),
                 sppName = name,
+                metricsCategory = metricsCategory,
                 displayName = displayName ?: name,
                 parameter = parameter,
                 arguments = arguments
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
index 0281ab8..95c7d23 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt
@@ -34,6 +34,10 @@
     /** The page provider name, needs to be *unique* and *stable*. */
     val name: String
 
+    /** The category id which is the PageId at SettingsEnums.*/
+    val metricsCategory: Int
+        get() = 0
+
     enum class NavType {
         Page,
         Dialog,
@@ -79,6 +83,7 @@
     return SettingsPage(
         id = genPageId(name, parameter, arguments),
         sppName = name,
+        metricsCategory = metricsCategory,
         displayName = displayName + parameter.normalizeArgList(arguments, eraseRuntimeValues = true)
             .joinToString("") { arg -> "/$arg" },
         parameter = parameter,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
index 215f6b9..7411297 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaLogger.kt
@@ -45,6 +45,7 @@
 internal const val LOG_DATA_SWITCH_STATUS = "switch"
 
 const val LOG_DATA_SESSION_NAME = "session"
+const val LOG_DATA_METRICS_CATEGORY = "metricsCategory"
 
 /**
  * The interface of logger in Spa
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
index 1a10bf0..2cf0d3b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/AnnotatedStringResource.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.LinkAnnotation
 import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextLinkStyles
 import androidx.compose.ui.text.buildAnnotatedString
 import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.text.font.FontWeight
@@ -98,7 +99,9 @@
 ) {
     val url = LinkAnnotation.Url(
         url = urlSpan.url,
-        style = SpanStyle(color = urlSpanColor, textDecoration = TextDecoration.Underline),
+        styles = TextLinkStyles(
+            style = SpanStyle(color = urlSpanColor, textDecoration = TextDecoration.Underline),
+        ),
     )
     addLink(url, start, end)
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
index a9e5e39..f323e99 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/PageLogger.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Composable
 import androidx.core.os.bundleOf
 import com.android.settingslib.spa.framework.common.LOG_DATA_DISPLAY_NAME
+import com.android.settingslib.spa.framework.common.LOG_DATA_METRICS_CATEGORY
 import com.android.settingslib.spa.framework.common.LOG_DATA_SESSION_NAME
 import com.android.settingslib.spa.framework.common.LogCategory
 import com.android.settingslib.spa.framework.common.LogEvent
@@ -45,9 +46,10 @@
         extraData = bundleOf(
             LOG_DATA_DISPLAY_NAME to displayName,
             LOG_DATA_SESSION_NAME to navController.sessionSourceName,
+            LOG_DATA_METRICS_CATEGORY to metricsCategory,
         ).apply {
             val normArguments = parameter.normalize(arguments)
             if (normArguments != null) putAll(normArguments)
         }
     )
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
index 612f9e5..4c1efd7 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/AnnotatedStringResourceTest.kt
@@ -21,6 +21,7 @@
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.LinkAnnotation
 import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextLinkStyles
 import androidx.compose.ui.text.font.FontStyle
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextDecoration
@@ -47,9 +48,11 @@
                 AnnotatedString.Range(
                     item = LinkAnnotation.Url(
                         url = "https://www.android.com/",
-                        style = SpanStyle(
-                            color = MaterialTheme.colorScheme.primary,
-                            textDecoration = TextDecoration.Underline,
+                        styles = TextLinkStyles(
+                            style = SpanStyle(
+                                color = MaterialTheme.colorScheme.primary,
+                                textDecoration = TextDecoration.Underline,
+                            ),
                         ),
                     ),
                     start = 31,
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
index 3dac7db..ea69eab 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBarTest.kt
@@ -61,11 +61,13 @@
 import com.android.settingslib.spa.testutils.rootWidth
 import com.android.settingslib.spa.testutils.setContentForSizeAssertions
 import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @OptIn(ExperimentalMaterial3Api::class)
+@Ignore("b/346785755")
 @RunWith(AndroidJUnit4::class)
 class CustomizedAppBarTest {
 
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
index dd7c036..a59a724 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUserTest.kt
@@ -23,8 +23,8 @@
 import android.os.UserHandle
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.lifecycle.testing.TestLifecycleOwner
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import org.junit.Rule
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
index c60ce41..b1baa86 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -31,6 +31,8 @@
 import android.os.BadParcelableException
 import android.os.DeadObjectException
 import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -76,9 +78,7 @@
     }
 
     private val mockUserManager = mock<UserManager> {
-        on { getUserInfo(ADMIN_USER_ID) } doReturn UserInfo().apply {
-            flags = UserInfo.FLAG_ADMIN
-        }
+        on { getUserInfo(ADMIN_USER_ID) } doReturn UserInfo(0, "admin", UserInfo.FLAG_ADMIN)
         on { getProfileIdsWithDisabled(ADMIN_USER_ID) } doReturn
             intArrayOf(ADMIN_USER_ID, MANAGED_PROFILE_USER_ID)
     }
@@ -281,9 +281,9 @@
         )
     }
 
+    @EnableFlags(Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
     @Test
     fun loadApps_hasApkInApexInfo_shouldNotIncludeAllHiddenApps() = runTest {
-        mSetFlagsRule.enableFlags(Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
         packageManager.stub {
             on { getInstalledModules(any()) } doReturn listOf(HIDDEN_MODULE)
         }
@@ -297,9 +297,9 @@
         assertThat(appList).containsExactly(NORMAL_APP)
     }
 
+    @DisableFlags(Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
     @Test
     fun loadApps_noApkInApexInfo_shouldNotIncludeHiddenSystemModule() = runTest {
-        mSetFlagsRule.disableFlags(Flags.FLAG_PROVIDE_INFO_OF_APK_IN_APEX)
         packageManager.stub {
             on { getInstalledModules(any()) } doReturn listOf(HIDDEN_MODULE)
         }
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
index ea3dbd9..0e71a1b 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -38,8 +38,9 @@
 import java.util.regex.Pattern;
 
 /**
- * Progres bar preference with a usage summary and a total summary.
- * This preference shows number in usage summary with enlarged font size.
+ * Progress bar preference with a usage summary and a total summary.
+ *
+ * <p>This preference shows number in usage summary with enlarged font size.
  */
 public class UsageProgressBarPreference extends Preference {
 
@@ -48,18 +49,18 @@
     private CharSequence mUsageSummary;
     private CharSequence mTotalSummary;
     private CharSequence mBottomSummary;
+    private CharSequence mBottomSummaryContentDescription;
     private ImageView mCustomImageView;
     private int mPercent = -1;
 
     /**
      * Perform inflation from XML and apply a class-specific base style.
      *
-     * @param context  The {@link Context} this is associated with, through which it can
-     *                 access the current theme, resources, {@link SharedPreferences}, etc.
-     * @param attrs    The attributes of the XML tag that is inflating the preference
+     * @param context The {@link Context} this is associated with, through which it can access the
+     *     current theme, resources, {@link SharedPreferences}, etc.
+     * @param attrs The attributes of the XML tag that is inflating the preference
      * @param defStyle An attribute in the current theme that contains a reference to a style
-     *                 resource that supplies default values for the view. Can be 0 to not
-     *                 look for defaults.
+     *     resource that supplies default values for the view. Can be 0 to not look for defaults.
      */
     public UsageProgressBarPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
@@ -69,9 +70,9 @@
     /**
      * Perform inflation from XML and apply a class-specific base style.
      *
-     * @param context The {@link Context} this is associated with, through which it can
-     *                access the current theme, resources, {@link SharedPreferences}, etc.
-     * @param attrs   The attributes of the XML tag that is inflating the preference
+     * @param context The {@link Context} this is associated with, through which it can access the
+     *     current theme, resources, {@link SharedPreferences}, etc.
+     * @param attrs The attributes of the XML tag that is inflating the preference
      */
     public UsageProgressBarPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -114,9 +115,17 @@
         notifyChanged();
     }
 
+    /** Set content description for the bottom summary. */
+    public void setBottomSummaryContentDescription(CharSequence contentDescription) {
+        if (!TextUtils.equals(mBottomSummaryContentDescription, contentDescription)) {
+            mBottomSummaryContentDescription = contentDescription;
+            notifyChanged();
+        }
+    }
+
     /** Set percentage of the progress bar. */
     public void setPercent(long usage, long total) {
-        if (usage >  total) {
+        if (usage > total) {
             return;
         }
         if (total == 0L) {
@@ -146,14 +155,13 @@
     /**
      * Binds the created View to the data for this preference.
      *
-     * <p>This is a good place to grab references to custom Views in the layout and set
-     * properties on them.
+     * <p>This is a good place to grab references to custom Views in the layout and set properties
+     * on them.
      *
      * <p>Make sure to call through to the superclass's implementation.
      *
      * @param holder The ViewHolder that provides references to the views to fill in. These views
-     *               will be recycled, so you should not hold a reference to them after this method
-     *               returns.
+     *     will be recycled, so you should not hold a reference to them after this method returns.
      */
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
@@ -177,6 +185,9 @@
             bottomSummary.setVisibility(View.VISIBLE);
             bottomSummary.setMovementMethod(LinkMovementMethod.getInstance());
             bottomSummary.setText(mBottomSummary);
+            if (!TextUtils.isEmpty(mBottomSummaryContentDescription)) {
+                bottomSummary.setContentDescription(mBottomSummaryContentDescription);
+            }
         }
 
         final ProgressBar progressBar = (ProgressBar) holder.findViewById(android.R.id.progress);
@@ -205,9 +216,12 @@
 
         final Matcher matcher = mNumberPattern.matcher(summary);
         if (matcher.find()) {
-            final SpannableString spannableSummary =  new SpannableString(summary);
-            spannableSummary.setSpan(new AbsoluteSizeSpan(64, true /* dip */), matcher.start(),
-                    matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            final SpannableString spannableSummary = new SpannableString(summary);
+            spannableSummary.setSpan(
+                    new AbsoluteSizeSpan(64, true /* dip */),
+                    matcher.start(),
+                    matcher.end(),
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
             return spannableSummary;
         }
         return summary;
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 3c034c2..4b11a76 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -96,7 +96,7 @@
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"متّصل (بدون هاتف أو وسائط)، ومستوى البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"‏البلوتوث نشِط. مستوى شحن البطارية: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"‏البلوتوث نشِط. مستوى الشحن في سماعة الرأس اليسرى: ‎<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_active_battery_level_untethered_left" msgid="2895644748625343977">"‏البلوتوث نشِط. مستوى الشحن في سماعة الرأس اليسرى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"‏نشِط. شحن سماعة الرأس اليسرى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <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>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index a723223..95586ea 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -84,7 +84,7 @@
     <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Адключана"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Адключэнне..."</string>
-    <string name="bluetooth_connecting" msgid="5871702668260192755">"Злучэнне..."</string>
+    <string name="bluetooth_connecting" msgid="5871702668260192755">"Падключэнне..."</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"Спалучэнне..."</string>
     <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без званкоў)"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index f0fb4df..caeee06 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -710,7 +710,7 @@
     <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Standard"</string>
     <string name="turn_screen_on_title" msgid="2662312432042116026">"Steuerelement zum Aktivieren des Displays"</string>
     <string name="allow_turn_screen_on" msgid="6194845766392742639">"Aktivieren des Displays erlauben"</string>
-    <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne deine explizite Absicht."</string>
+    <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne dass du dies beabsichtigst."</string>
     <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string>
     <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string>
     <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> streamen"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 3c63119..0b96faa 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -127,7 +127,7 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
-    <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Acceso a contactos e historial de llamadas"</string>
+    <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Permitir acceso a contactos e historial de llamadas"</string>
     <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"La información se usará para avisos de llamada y más"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 647309b..a0dc830 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -632,7 +632,7 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"Profiilin tiedot"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"Ennen kuin voit luoda rajoitetun profiilin, määritä näytön lukitus, joka suojelee sovelluksiasi ja henkilökohtaisia tietojasi."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Aseta lukitus"</string>
-    <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Luodaan uutta käyttäjää…"</string>
     <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"Luodaan uutta vierasta…"</string>
     <string name="add_user_failed" msgid="4809887794313944872">"Uuden käyttäjän luominen epäonnistui"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 874a153..3a08d2f 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -95,9 +95,9 @@
     <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Conectado a <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (sen audio multimedia), batería ao <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Conectado a <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (sen teléfono nin audio multimedia), batería ao <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Activo. <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
-    <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Activo. Esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería. Dereito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería."</string>
-    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Activo. Esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
-    <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Activo. Dereito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
+    <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Activo. E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería. D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería."</string>
+    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Activo. E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
+    <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Activo. D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Batería: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería. Dereito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería."</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 1381903..a25e179 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -632,7 +632,7 @@
     <string name="profile_info_settings_title" msgid="105699672534365099">"ಪ್ರೊಫೈಲ್‌‌ ಮಾಹಿತಿ"</string>
     <string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್‌ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string>
-    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string>
+    <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>ಗೆ ಬದಲಿಸಿ"</string>
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="add_user_failed" msgid="4809887794313944872">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 50f0c2e..9da1d60 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -236,7 +236,7 @@
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Pilih profil"</string>
     <string name="category_personal" msgid="6236798763159385225">"Peribadi"</string>
-    <string name="category_work" msgid="4014193632325996115">"Tempat Kerja"</string>
+    <string name="category_work" msgid="4014193632325996115">"Kerja"</string>
     <string name="category_private" msgid="4244892185452788977">"Persendirian"</string>
     <string name="category_clone" msgid="1554511758987195974">"Klon"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Pilihan pembangun"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 5032e35..1e33f88 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -641,7 +641,7 @@
     <string name="edit_user_info_message" msgid="6677556031419002895">"यो डिभाइस प्रयोग गर्ने सबै जना तपाईंले छनौट गर्ने नाम र फोटो देख्न सक्ने छन्।"</string>
     <string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता कनेक्ट गर्नुहोस्"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि कनेक्ट गर्नुहोस्"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"गेस्ट मोडबाट बाहिर निस्कियोस्"</string>
+    <string name="guest_exit_guest" msgid="5908239569510734136">"अथिति हटाउनुहोस्"</string>
     <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथि सत्र रिसेट गर्नुहोस्"</string>
     <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गर्ने हो?"</string>
     <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"यी अतिथि हटाउने हो?"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index bfec96c..f80ec8d 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -422,7 +422,7 @@
     <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"ਐਪ ਵੱਲੋਂ ਵੈਧ ਚੈਨਲ ਤੋਂ ਬਿਨਾਂ ਸੂਚਨਾ ਪੋਸਟ ਕਰਨ \'ਤੇ ਸਕ੍ਰੀਨ \'ਤੇ ਚਿਤਾਵਨੀ ਦਿਖਾਉਂਦੀ ਹੈ"</string>
     <string name="force_allow_on_external" msgid="9187902444231637880">"ਐਪਾਂ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਬਾਹਰੀ ਸਟੋਰੇਜ \'ਤੇ ਆਗਿਆ ਦਿਓ"</string>
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"ਮੈਨੀਫੈਸਟ ਮੁੱਲਾਂ ਦੀ ਪਰਵਾਹ ਕੀਤੇ ਬਿਨਾਂ, ਕਿਸੇ ਵੀ ਐਪ ਨੂੰ ਬਾਹਰੀ ਸਟੋਰੇਜ \'ਤੇ ਲਿਖਣ ਦੇ ਯੋਗ ਬਣਾਉਂਦੀ ਹੈ"</string>
-    <string name="force_resizable_activities" msgid="7143612144399959606">"ਵਿੰਡੋ ਮੁਤਾਬਕ ਸਰਗਰਮੀਆਂ ਦਾ ਆਕਾਰ ਬਦਲਣ ਦਿਓ"</string>
+    <string name="force_resizable_activities" msgid="7143612144399959606">"ਸਰਗਰਮੀਆਂ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਆਕਾਰ ਬਦਲਣਯੋਗ ਬਣਾਓ"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"ਮੈਨੀਫ਼ੈਸਟ ਮੁੱਲਾਂ ਦੀ ਪਰਵਾਹ ਕੀਤੇ ਬਿਨਾਂ, ਮਲਟੀ-ਵਿੰਡੋ ਲਈ ਸਾਰੀਆਂ ਸਰਗਰਮੀਆਂ ਨੂੰ ਆਕਾਰ ਬਦਲਣਯੋਗ ਬਣਾਓ।"</string>
     <string name="enable_freeform_support" msgid="7599125687603914253">"ਫ੍ਰੀਫਾਰਮ ਵਿੰਡੋਜ਼ ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
     <string name="enable_freeform_support_summary" msgid="1822862728719276331">"ਪ੍ਰਯੋਗਮਈ ਫ੍ਰੀਫਾਰਮ ਵਿੰਡੋਜ਼ ਲਈ ਸਮਰਥਨ ਨੂੰ ਚਾਲੂ ਕਰੋ।"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index e76be70..8babb8b 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -288,7 +288,7 @@
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Zezwolić na zdjęcie blokady OEM?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"UWAGA: gdy to ustawienie jest włączone, na urządzeniu nie będą działać funkcje ochrony."</string>
     <string name="mock_location_app" msgid="6269380172542248304">"Aplikacja do pozorowania lokalizacji"</string>
-    <string name="mock_location_app_not_set" msgid="6972032787262831155">"Nie wybrano aplikacji do pozorowania lokalizacji"</string>
+    <string name="mock_location_app_not_set" msgid="6972032787262831155">"Nie wybrano aplikacji"</string>
     <string name="mock_location_app_set" msgid="4706722469342913843">"Aplikacja do pozorowania lokalizacji: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="6829757985772659599">"Sieci"</string>
     <string name="wifi_display_certification" msgid="1805579519992520381">"Certyfikacja wyświetlacza bezprzewodowego"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index d47a1f4..337b011 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -237,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string>
     <string name="category_personal" msgid="6236798763159385225">"Pessoal"</string>
     <string name="category_work" msgid="4014193632325996115">"Trabalho"</string>
-    <string name="category_private" msgid="4244892185452788977">"Particular"</string>
+    <string name="category_private" msgid="4244892185452788977">"Privado"</string>
     <string name="category_clone" msgid="1554511758987195974">"Clone"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index d47a1f4..337b011 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -237,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string>
     <string name="category_personal" msgid="6236798763159385225">"Pessoal"</string>
     <string name="category_work" msgid="4014193632325996115">"Trabalho"</string>
-    <string name="category_private" msgid="4244892185452788977">"Particular"</string>
+    <string name="category_private" msgid="4244892185452788977">"Privado"</string>
     <string name="category_clone" msgid="1554511758987195974">"Clone"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 1c76fdd..4bddeb9a 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -94,7 +94,7 @@
     <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Conectat (fără telefon), baterie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Conectat (fără conținut media), baterie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Conectat (fără telefon sau conținut media), baterie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Activ. Nivelul bateriei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Activ. Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Activ. Nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Activ. Nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Activ. Nivelul bateriei din dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index f7d1ce2..f30a125 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -96,8 +96,8 @@
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Подключено (кроме звонков и аудио), уровень заряда батареи: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Используется, заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Используется, заряд: <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_active_battery_level_untethered_left" msgid="2895644748625343977">"Используется, заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (Л)"</string>
-    <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_active_battery_level_untethered_left" msgid="2895644748625343977">"Активно, заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (Л)"</string>
+    <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>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index c33e425..3db7146 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -96,8 +96,8 @@
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"E lidhur (pa telefon ose media), bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Aktiv. <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri."</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Aktiv. Majtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> bateri, djathtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> bateri."</string>
-    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Aktive. Majtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (niveli i baterisë)."</string>
-    <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Aktive. Djathtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (niveli i baterisë)."</string>
+    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Aktive. Majtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri."</string>
+    <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Aktive. Djathtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri."</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"Bateria <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> bateri"</string>
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Majtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> bateri, djathtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> bateri."</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 00f4349..01b786d 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -96,7 +96,7 @@
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Imeunganishwa (hamna simu au kifaa cha sauti), kiasi cha chaji ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Inatumika. Chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Inatumika. Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
-    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Inatumika. Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Inatumika. Kushoto: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Inatumika. Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index ff62ba0..8aaeb5d 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -161,7 +161,7 @@
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ரத்துசெய்"</string>
     <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"இணைத்தலானது உங்கள் தொடர்புகள், அழைப்பு வரலாறுக்கான அணுகலை வழங்குகிறது."</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைய முடியவில்லை."</string>
-    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சொல் காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
+    <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சாவி காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> இணைப்பதை நிராகரித்தது."</string>
     <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"கணினி"</string>
@@ -237,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"சுயவிவரத்தைத் தேர்வு செய்க"</string>
     <string name="category_personal" msgid="6236798763159385225">"தனிப்பட்டவை"</string>
     <string name="category_work" msgid="4014193632325996115">"பணியிடம்"</string>
-    <string name="category_private" msgid="4244892185452788977">"தனிப்பட்டவை"</string>
+    <string name="category_private" msgid="4244892185452788977">"இரகசியமானவை"</string>
     <string name="category_clone" msgid="1554511758987195974">"குளோன்"</string>
     <string name="development_settings_title" msgid="140296922921597393">"டெவெலப்பர் விருப்பங்கள்"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"டெவெலப்பர் விருப்பங்களை இயக்கு"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 6aea659..e8bac31 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -492,9 +492,9 @@
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
     <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string>
-    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> içinde tamamen dolacak"</string>
-    <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> içinde tamamen şarj olacak"</string>
-    <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"<xliff:g id="TIME">%1$s</xliff:g> içinde tamamen şarj olacak"</string>
+    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Tamamen dolacağı zaman: <xliff:g id="TIME">%3$s</xliff:g>"</string>
+    <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olacağı zaman: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Tamamen şarj olacağı zaman: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"<xliff:g id="TIME">%1$s</xliff:g> itibarıyla tamamen dolacak"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index d7cc91e..9bb4ef5 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -96,8 +96,8 @@
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Đã kết nối (không có điện thoại hoặc phương tiện), mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Đang hoạt động. Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin."</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Đang hoạt động. Bên trái: Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> pin. Bên phải: Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pin."</string>
-    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Đang hoạt động. Tai trái: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin."</string>
-    <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Đang hoạt động. Tai phải: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin."</string>
+    <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Đang hoạt động. Trái: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin."</string>
+    <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Đang hoạt động. Phải: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin."</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"Mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Bên trái: Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> pin. Bên phải: Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pin."</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index e16e46a..c1d5481 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -96,8 +96,8 @@
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"已连接(无手机或媒体信号),电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"使用中。电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>。"</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"使用中。左侧电池电量为 <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_active_battery_level_untethered_left" msgid="2895644748625343977">"使用中。左侧剩余电量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>。"</string>
-    <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_active_battery_level_untethered_left" msgid="2895644748625343977">"使用中。左侧电量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>。"</string>
+    <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>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 363045e..d373201 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -953,6 +953,18 @@
     <string name="enable_verbose_vendor_logging">Enable verbose vendor logging</string>
     <!-- UI debug setting: enable verbose vendor logging summary [CHAR LIMIT=NONE] -->
     <string name="enable_verbose_vendor_logging_summary">Include additional device-specific vendor logs in bug reports, which may contain private information, use more battery, and/or use more storage.</string>
+    <!-- UI debug setting: checkbox text to disable verbose vendor logging after one day [CHAR LIMIT=50] -->
+    <string name="enable_verbose_vendor_logging_checkbox">Disable after one day</string>
+    <!-- UI debug setting: verbose vendor logging notification title [CHAR LIMIT=60] -->
+    <string name="verbose_vendor_logging_notification_title">Verbose vendor logging has ended</string>
+    <!-- UI debug setting: verbose vendor logging notification summary [CHAR LIMIT=60] -->
+    <string name="verbose_vendor_logging_notification_summary">Enabled for one day</string>
+    <!-- UI debug setting: verbose vendor logging notification action text [CHAR LIMIT=60] -->
+    <string name="verbose_vendor_logging_notification_action">Enable for one more day</string>
+    <!-- UI debug setting: verbose vendor logging preference summary for disabling after one day [CHAR LIMIT=60] -->
+    <string name="verbose_vendor_logging_preference_summary_will_disable">Disables after one day</string>
+    <!-- UI debug setting: verbose vendor logging preference summary for leaving setting enabled indefinitely [CHAR LIMIT=60] -->
+    <string name="verbose_vendor_logging_preference_summary_on">Enabled indefinitely</string>
 
     <!-- UI debug setting: scaling factor for window animations [CHAR LIMIT=25] -->
     <string name="window_animation_scale_title">Window animation scale</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index ed964a9..b3e48b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -209,44 +209,34 @@
                 CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice);
                 if (mainDevice != null) {
                     if (mainDevice.isConnected()) {
-                        // When main device exists and in connected state, receiving sub device
-                        // connection. To refresh main device UI
+                        // Sub/member device is connected and main device is connected
+                        // To refresh main device UI
                         mainDevice.refresh();
                     } else {
-                        // When both Hearing Aid devices are disconnected, receiving sub device
-                        // connection. To switch content and dispatch to notify UI change
-                        mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
-                        mainDevice.switchSubDeviceContent();
-                        mainDevice.refresh();
-                        // It is necessary to do remove and add for updating the mapping on
-                        // preference and device
-                        mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
+                        // Sub/member device is connected and main device is disconnected
+                        // To switch content and dispatch to notify UI change
+                        switchDeviceContent(mainDevice, cachedDevice);
                     }
                     return true;
                 }
                 break;
             case BluetoothProfile.STATE_DISCONNECTED:
-                mainDevice = findMainDevice(cachedDevice);
                 if (cachedDevice.getUnpairing()) {
                     return true;
                 }
+                mainDevice = findMainDevice(cachedDevice);
                 if (mainDevice != null) {
-                    // When main device exists, receiving sub device disconnection
+                    // Sub/member device is disconnected and main device exists
                     // To update main device UI
                     mainDevice.refresh();
                     return true;
                 }
-                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
-                if (subDevice != null && subDevice.isConnected()) {
-                    // Main device is disconnected and sub device is connected
-                    // To copy data from sub device to main device
-                    mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
-                    cachedDevice.switchSubDeviceContent();
-                    cachedDevice.refresh();
-                    // It is necessary to do remove and add for updating the mapping on
-                    // preference and device
-                    mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice);
-
+                CachedBluetoothDevice connectedSecondaryDevice = getConnectedSecondaryDevice(
+                        cachedDevice);
+                if (connectedSecondaryDevice != null) {
+                    // Main device is disconnected and sub/member device is connected
+                    // To switch content and dispatch to notify UI change
+                    switchDeviceContent(cachedDevice, connectedSecondaryDevice);
                     return true;
                 }
                 break;
@@ -254,6 +244,29 @@
         return false;
     }
 
+    private void switchDeviceContent(CachedBluetoothDevice mainDevice,
+            CachedBluetoothDevice secondaryDevice) {
+        mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
+        if (mainDevice.getSubDevice() != null
+                && mainDevice.getSubDevice().equals(secondaryDevice)) {
+            mainDevice.switchSubDeviceContent();
+        } else {
+            mainDevice.switchMemberDeviceContent(secondaryDevice);
+        }
+        mainDevice.refresh();
+        // It is necessary to do remove and add for updating the mapping on
+        // preference and device
+        mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
+    }
+
+    private CachedBluetoothDevice getConnectedSecondaryDevice(CachedBluetoothDevice cachedDevice) {
+        if (cachedDevice.getSubDevice() != null && cachedDevice.getSubDevice().isConnected()) {
+            return cachedDevice.getSubDevice();
+        }
+        return cachedDevice.getMemberDevice().stream().filter(
+                CachedBluetoothDevice::isConnected).findAny().orElse(null);
+    }
+
     void onActiveDeviceChanged(CachedBluetoothDevice device) {
         if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) {
             if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice(
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index cdb8740..063807a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -17,6 +17,8 @@
 
 import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHearingAid;
@@ -37,21 +39,14 @@
 
     private static final String TAG = "BluetoothMediaDevice";
 
-    private CachedBluetoothDevice mCachedDevice;
+    private final CachedBluetoothDevice mCachedDevice;
     private final AudioManager mAudioManager;
 
     BluetoothMediaDevice(
-            Context context,
-            CachedBluetoothDevice device,
-            MediaRoute2Info info) {
-        this(context, device, info, null);
-    }
-
-    BluetoothMediaDevice(
-            Context context,
-            CachedBluetoothDevice device,
-            MediaRoute2Info info,
-            RouteListingPreference.Item item) {
+            @NonNull Context context,
+            @NonNull CachedBluetoothDevice device,
+            @Nullable MediaRoute2Info info,
+            @Nullable RouteListingPreference.Item item) {
         super(context, info, item);
         mCachedDevice = device;
         mAudioManager = context.getSystemService(AudioManager.class);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
index 338fb87..a87daf9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.media;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
@@ -32,9 +34,9 @@
     private final String mSummary = "";
 
     ComplexMediaDevice(
-            Context context,
-            MediaRoute2Info info,
-            RouteListingPreference.Item item) {
+            @NonNull Context context,
+            @NonNull MediaRoute2Info info,
+            @Nullable RouteListingPreference.Item item) {
         super(context, info, item);
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
index 3de4933..717a8ee 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/DeviceIconUtil.java
@@ -16,135 +16,136 @@
 
 package com.android.settingslib.media;
 
+import static android.media.AudioDeviceInfo.AudioDeviceType;
+import static android.media.AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+import static android.media.AudioDeviceInfo.TYPE_DOCK;
+import static android.media.AudioDeviceInfo.TYPE_HDMI;
+import static android.media.AudioDeviceInfo.TYPE_HDMI_ARC;
+import static android.media.AudioDeviceInfo.TYPE_HDMI_EARC;
+import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY;
+import static android.media.AudioDeviceInfo.TYPE_USB_DEVICE;
+import static android.media.AudioDeviceInfo.TYPE_USB_HEADSET;
+import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES;
+import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET;
+
 import android.annotation.DrawableRes;
+import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
-import android.media.AudioDeviceInfo;
 import android.media.MediaRoute2Info;
+import android.os.SystemProperties;
+import android.util.SparseIntArray;
 
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.R;
 import com.android.settingslib.media.flags.Flags;
 
 import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.Objects;
 
 /** A util class to get the appropriate icon for different device types. */
 public class DeviceIconUtil {
 
-    // A default icon to use if the type is not present in the map.
-    @DrawableRes private static final int DEFAULT_ICON = R.drawable.ic_smartphone;
-    @DrawableRes private static final int DEFAULT_ICON_TV = R.drawable.ic_media_speaker_device;
-
-    // A map from a @AudioDeviceInfo.AudioDeviceType to full device information.
-    private final Map<Integer, Device> mAudioDeviceTypeToIconMap = new HashMap<>();
-    // A map from a @MediaRoute2Info.Type to full device information.
-    private final Map<Integer, Device> mMediaRouteTypeToIconMap = new HashMap<>();
+    private static final SparseIntArray AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE = new SparseIntArray();
 
     private final boolean mIsTv;
-
-    public DeviceIconUtil(Context context) {
-        this(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+    private final boolean mIsTablet;
+    private final Context mContext;
+    public DeviceIconUtil(@NonNull Context context) {
+        mContext = Objects.requireNonNull(context);
+        mIsTv =
+                mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                        && Flags.enableTvMediaOutputDialog();
+        mIsTablet =
+                Arrays.asList(SystemProperties.get("ro.build.characteristics").split(","))
+                        .contains("tablet");
     }
 
-    public DeviceIconUtil(boolean isTv) {
-        mIsTv = isTv && Flags.enableTvMediaOutputDialog();
-        List<Device> deviceList = Arrays.asList(
-                        new Device(
-                                AudioDeviceInfo.TYPE_USB_DEVICE,
-                                MediaRoute2Info.TYPE_USB_DEVICE,
-                                R.drawable.ic_headphone),
-                        new Device(
-                                AudioDeviceInfo.TYPE_USB_HEADSET,
-                                MediaRoute2Info.TYPE_USB_HEADSET,
-                                R.drawable.ic_headphone),
-                        new Device(
-                                AudioDeviceInfo.TYPE_USB_ACCESSORY,
-                                MediaRoute2Info.TYPE_USB_ACCESSORY,
-                                mIsTv ? R.drawable.ic_usb : R.drawable.ic_headphone),
-                        new Device(
-                                AudioDeviceInfo.TYPE_DOCK,
-                                MediaRoute2Info.TYPE_DOCK,
-                                R.drawable.ic_dock_device),
-                        new Device(
-                                AudioDeviceInfo.TYPE_HDMI,
-                                MediaRoute2Info.TYPE_HDMI,
-                                mIsTv ? R.drawable.ic_tv : R.drawable.ic_external_display),
-                        new Device(
-                                AudioDeviceInfo.TYPE_HDMI_ARC,
-                                MediaRoute2Info.TYPE_HDMI_ARC,
-                                mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_external_display),
-                        new Device(
-                                AudioDeviceInfo.TYPE_HDMI_EARC,
-                                MediaRoute2Info.TYPE_HDMI_EARC,
-                                mIsTv ? R.drawable.ic_hdmi : R.drawable.ic_external_display),
-                        new Device(
-                                AudioDeviceInfo.TYPE_WIRED_HEADSET,
-                                MediaRoute2Info.TYPE_WIRED_HEADSET,
-                                mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone),
-                        new Device(
-                                AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
-                                MediaRoute2Info.TYPE_WIRED_HEADPHONES,
-                                mIsTv ? R.drawable.ic_wired_device : R.drawable.ic_headphone),
-                        new Device(
-                                AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
-                                MediaRoute2Info.TYPE_BUILTIN_SPEAKER,
-                                mIsTv ? R.drawable.ic_tv : R.drawable.ic_smartphone));
-        for (int i = 0; i < deviceList.size(); i++) {
-            Device device = deviceList.get(i);
-            mAudioDeviceTypeToIconMap.put(device.mAudioDeviceType, device);
-            mMediaRouteTypeToIconMap.put(device.mMediaRouteType, device);
-        }
-    }
-
-    private int getDefaultIcon() {
-        return mIsTv ? DEFAULT_ICON_TV : DEFAULT_ICON;
+    @VisibleForTesting
+    /* package */ DeviceIconUtil(boolean isTv) {
+        mContext = null;
+        mIsTv = isTv;
+        mIsTablet = false;
     }
 
     /** Returns a drawable for an icon representing the given audioDeviceType. */
-    public Drawable getIconFromAudioDeviceType(
-            @AudioDeviceInfo.AudioDeviceType int audioDeviceType, Context context) {
-        return context.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType));
+    public Drawable getIconFromAudioDeviceType(@AudioDeviceType int audioDeviceType) {
+        return mContext.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType));
     }
 
     /** Returns a drawable res ID for an icon representing the given audioDeviceType. */
     @DrawableRes
-    public int getIconResIdFromAudioDeviceType(
-            @AudioDeviceInfo.AudioDeviceType int audioDeviceType) {
-        if (mAudioDeviceTypeToIconMap.containsKey(audioDeviceType)) {
-            return mAudioDeviceTypeToIconMap.get(audioDeviceType).mIconDrawableRes;
-        }
-        return getDefaultIcon();
+    public int getIconResIdFromAudioDeviceType(@AudioDeviceType int audioDeviceType) {
+        int mediaRouteType =
+                AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.get(audioDeviceType, /* defaultValue */ -1);
+        return getIconResIdFromMediaRouteType(mediaRouteType);
     }
 
     /** Returns a drawable res ID for an icon representing the given mediaRouteType. */
     @DrawableRes
-    public int getIconResIdFromMediaRouteType(
-            @MediaRoute2Info.Type int mediaRouteType) {
-        if (mMediaRouteTypeToIconMap.containsKey(mediaRouteType)) {
-            return mMediaRouteTypeToIconMap.get(mediaRouteType).mIconDrawableRes;
-        }
-        return getDefaultIcon();
+    public int getIconResIdFromMediaRouteType(@MediaRoute2Info.Type int type) {
+        return mIsTv
+                ? getIconResourceIdForTv(type)
+                : getIconResourceIdForPhoneOrTablet(type, mIsTablet);
     }
 
-    private static class Device {
-        @AudioDeviceInfo.AudioDeviceType
-        private final int mAudioDeviceType;
+    @SuppressLint("SwitchIntDef")
+    @DrawableRes
+    private static int getIconResourceIdForPhoneOrTablet(
+            @MediaRoute2Info.Type int type, boolean isTablet) {
+        int defaultResId = isTablet ? R.drawable.ic_media_tablet : R.drawable.ic_smartphone;
 
-        @MediaRoute2Info.Type
-        private final int mMediaRouteType;
+        return switch (type) {
+            case MediaRoute2Info.TYPE_USB_DEVICE,
+                            MediaRoute2Info.TYPE_USB_HEADSET,
+                            MediaRoute2Info.TYPE_USB_ACCESSORY,
+                            MediaRoute2Info.TYPE_WIRED_HEADSET,
+                            MediaRoute2Info.TYPE_WIRED_HEADPHONES ->
+                    R.drawable.ic_headphone;
+            case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device;
+            case MediaRoute2Info.TYPE_HDMI,
+                            MediaRoute2Info.TYPE_HDMI_ARC,
+                            MediaRoute2Info.TYPE_HDMI_EARC ->
+                    R.drawable.ic_external_display;
+            default -> defaultResId; // Includes TYPE_BUILTIN_SPEAKER.
+        };
+    }
 
-        @DrawableRes
-        private final int mIconDrawableRes;
+    @SuppressLint("SwitchIntDef")
+    @DrawableRes
+    private static int getIconResourceIdForTv(@MediaRoute2Info.Type int type) {
+        return switch (type) {
+            case MediaRoute2Info.TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_HEADSET ->
+                    R.drawable.ic_headphone;
+            case MediaRoute2Info.TYPE_USB_ACCESSORY -> R.drawable.ic_usb;
+            case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device;
+            case MediaRoute2Info.TYPE_HDMI, MediaRoute2Info.TYPE_BUILTIN_SPEAKER ->
+                    R.drawable.ic_tv;
+            case MediaRoute2Info.TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_EARC ->
+                    R.drawable.ic_hdmi;
+            case MediaRoute2Info.TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADPHONES ->
+                    R.drawable.ic_wired_device;
+            default -> R.drawable.ic_media_speaker_device;
+        };
+    }
 
-        Device(@AudioDeviceInfo.AudioDeviceType int audioDeviceType,
-                @MediaRoute2Info.Type int mediaRouteType,
-                @DrawableRes int iconDrawableRes) {
-            mAudioDeviceType = audioDeviceType;
-            mMediaRouteType = mediaRouteType;
-            mIconDrawableRes = iconDrawableRes;
-        }
+    static {
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_DEVICE);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_HEADSET, MediaRoute2Info.TYPE_USB_HEADSET);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
+                TYPE_USB_ACCESSORY, MediaRoute2Info.TYPE_USB_ACCESSORY);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_DOCK, MediaRoute2Info.TYPE_DOCK);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI, MediaRoute2Info.TYPE_HDMI);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_ARC);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_EARC, MediaRoute2Info.TYPE_HDMI_EARC);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
+                TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADSET);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
+                TYPE_WIRED_HEADPHONES, MediaRoute2Info.TYPE_WIRED_HEADPHONES);
+        AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
+                TYPE_BUILTIN_SPEAKER, MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 1347dd1..21873ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -26,6 +26,8 @@
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TABLET_DOCKED;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
@@ -43,17 +45,13 @@
     private static final String TAG = "InfoMediaDevice";
 
     InfoMediaDevice(
-            Context context,
-            MediaRoute2Info info,
-            RouteListingPreference.Item item) {
+            @NonNull Context context,
+            @NonNull MediaRoute2Info info,
+            @Nullable RouteListingPreference.Item item) {
         super(context, info, item);
         initDeviceRecord();
     }
 
-    InfoMediaDevice(Context context, MediaRoute2Info info) {
-        this(context, info, null);
-    }
-
     @Override
     public String getName() {
         return mRouteInfo.getName().toString();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index b7758de..4e1d8e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -671,7 +671,7 @@
     // MediaRoute2Info.getType was made public on API 34, but exists since API 30.
     @SuppressWarnings("NewApi")
     @VisibleForTesting
-    void addMediaDevice(MediaRoute2Info route, RoutingSessionInfo activeSession) {
+    void addMediaDevice(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo activeSession) {
         final int deviceType = route.getType();
         MediaDevice mediaDevice = null;
         switch (deviceType) {
@@ -711,8 +711,13 @@
             case TYPE_HEARING_AID:
             case TYPE_BLUETOOTH_A2DP:
             case TYPE_BLE_HEADSET:
+                if (route.getAddress() == null) {
+                    Log.e(TAG, "Ignoring bluetooth route with no set address: " + route);
+                    break;
+                }
                 final BluetoothDevice device =
-                        BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getAddress());
+                        BluetoothAdapter.getDefaultAdapter()
+                                .getRemoteDevice(route.getAddress());
                 final CachedBluetoothDevice cachedDevice =
                         mBluetoothManager.getCachedDeviceManager().findDevice(device);
                 if (cachedDevice != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index cfa825b..72a60fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -570,7 +570,7 @@
                 final CachedBluetoothDevice cachedDevice =
                         cachedDeviceManager.findDevice(device);
                 if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) {
-                    return new BluetoothMediaDevice(mContext, cachedDevice, null);
+                    return new BluetoothMediaDevice(mContext, cachedDevice, null, /* item */ null);
                 }
             }
             return null;
@@ -617,7 +617,7 @@
             mDisconnectedMediaDevices.clear();
             for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) {
                 final MediaDevice mediaDevice =
-                        new BluetoothMediaDevice(mContext, cachedDevice, null);
+                        new BluetoothMediaDevice(mContext, cachedDevice, null, /* item */ null);
                 if (!mMediaDevices.contains(mediaDevice)) {
                     cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
                     mDisconnectedMediaDevices.add(mediaDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 0c4cf76..ce1f297 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -49,6 +49,8 @@
 import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
 import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -123,9 +125,9 @@
     protected final RouteListingPreference.Item mItem;
 
     MediaDevice(
-            Context context,
-            MediaRoute2Info info,
-            RouteListingPreference.Item item) {
+            @NonNull Context context,
+            @Nullable MediaRoute2Info info,
+            @Nullable RouteListingPreference.Item item) {
         mContext = context;
         mRouteInfo = info;
         mItem = item;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index ba9180d..9eaf8d3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -29,6 +29,8 @@
 import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
 
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
@@ -40,7 +42,6 @@
 import android.os.SystemProperties;
 import android.util.Log;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settingslib.R;
@@ -100,17 +101,6 @@
                         R.string.media_transfer_external_device_name);
                 break;
             case TYPE_HDMI_ARC:
-                if (isTv) {
-                    String deviceName = getHdmiOutDeviceName(context);
-                    if (deviceName != null) {
-                        name = deviceName;
-                    } else {
-                        name = context.getString(R.string.tv_media_transfer_arc_fallback_title);
-                    }
-                } else {
-                    name = context.getString(R.string.media_transfer_external_device_name);
-                }
-                break;
             case TYPE_HDMI_EARC:
                 if (isTv) {
                     String deviceName = getHdmiOutDeviceName(context);
@@ -130,14 +120,10 @@
         return name.toString();
     }
 
-    PhoneMediaDevice(Context context, MediaRoute2Info info) {
-        this(context, info, null);
-    }
-
     PhoneMediaDevice(
-            Context context,
-            MediaRoute2Info info,
-            RouteListingPreference.Item item) {
+            @NonNull Context context,
+            @NonNull MediaRoute2Info info,
+            @Nullable RouteListingPreference.Item item) {
         super(context, info, item);
         mDeviceIconUtil = new DeviceIconUtil(mContext);
         initDeviceRecord();
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 8204569..20b949f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -37,6 +37,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterIsInstance
@@ -161,6 +162,7 @@
                 },
                 volumeSettingChanges(audioStream),
             )
+            .conflate()
             .map { getCurrentAudioStream(audioStream) }
             .onStart { emit(getCurrentAudioStream(audioStream)) }
             .flowOn(backgroundCoroutineContext)
@@ -184,10 +186,11 @@
         }
     }
 
-    override suspend fun setVolume(audioStream: AudioStream, volume: Int) =
+    override suspend fun setVolume(audioStream: AudioStream, volume: Int) {
         withContext(backgroundCoroutineContext) {
             audioManager.setStreamVolume(audioStream.value, volume, 0)
         }
+    }
 
     override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean {
         return withContext(backgroundCoroutineContext) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 4188d2e..bf927a1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -681,6 +681,53 @@
         verify(mCachedDevice1).refresh();
     }
 
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When main device is disconnected, to verify switch() result for member device connected
+     * event
+     */
+    @Test
+    public void onProfileConnectionStateChanged_connect_member_mainDisconnected_switch() {
+        when(mCachedDevice1.isConnected()).thenReturn(false);
+        when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
+        when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.addMemberDevice(mCachedDevice2);
+
+        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
+        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();
+
+        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
+        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
+        verify(mCachedDevice1).refresh();
+    }
+
+    /**
+     * Test onProfileConnectionStateChangedIfProcessed.
+     * When member device is connected, to verify switch() result for main device disconnected
+     * event
+     */
+    @Test
+    public void onProfileConnectionStateChanged_disconnect_main_subDeviceConnected_switch() {
+        when(mCachedDevice2.isConnected()).thenReturn(true);
+        when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1);
+        when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mCachedDevice1.addMemberDevice(mCachedDevice2);
+
+        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
+        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
+        assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
+                mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue();
+
+        assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
+        assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
+        verify(mCachedDevice1).refresh();
+    }
+
     @Test
     public void onActiveDeviceChanged_connected_callSetStrategies() {
         when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
index 8edda1a..883640d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/DeviceIconUtilTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.content.Context;
 import android.media.AudioDeviceInfo;
 import android.media.MediaRoute2Info;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -30,6 +31,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowSystemProperties;
 
 @RunWith(RobolectricTestRunner.class)
 public class DeviceIconUtilTest {
@@ -37,9 +40,12 @@
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
+    private Context mContext;
+
     @Before
     public void setup() {
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
+        mContext = RuntimeEnvironment.getApplication();
     }
 
     @Test
@@ -171,6 +177,14 @@
     }
 
     @Test
+    public void getIconResIdFromMediaRouteType_onTablet_builtinSpeaker_isTablet() {
+        ShadowSystemProperties.override("ro.build.characteristics", "tablet");
+        assertThat(new DeviceIconUtil(mContext)
+                .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
+                .isEqualTo(R.drawable.ic_media_tablet);
+    }
+
+    @Test
     public void getIconResIdFromMediaRouteType_unsupportedType_isSmartphone() {
         assertThat(new DeviceIconUtil(/* isTv */ false)
                 .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
@@ -178,6 +192,14 @@
     }
 
     @Test
+    public void getIconResIdFromMediaRouteType_onTablet_unsupportedType_isTablet() {
+        ShadowSystemProperties.override("ro.build.characteristics", "tablet");
+        assertThat(new DeviceIconUtil(mContext)
+                .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
+                .isEqualTo(R.drawable.ic_media_tablet);
+    }
+
+    @Test
     public void getIconResIdFromMediaRouteType_tv_unsupportedType_isSpeaker() {
         assertThat(new DeviceIconUtil(/* isTv */ true)
                 .getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index 0665308..6647a27 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -65,7 +65,7 @@
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
 
-        mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo);
+        mInfoMediaDevice = new InfoMediaDevice(mContext, mRouteInfo, /* item */ null);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index ce07fe9..c9b35a0a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -559,7 +559,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, /* item */ null);
 
         final List<String> list = new ArrayList<>();
         list.add(TEST_ID);
@@ -580,7 +580,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, /* item */ null);
 
         final List<String> list = new ArrayList<>();
         list.add("fake_id");
@@ -602,7 +602,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, /* item */ null);
 
         final List<String> list = new ArrayList<>();
         list.add(TEST_ID);
@@ -623,7 +623,7 @@
         routingSessionInfos.add(info);
 
         final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, route2Info);
+        final MediaDevice device = new InfoMediaDevice(mContext, route2Info, /* item */ null);
 
         final List<String> list = new ArrayList<>();
         list.add("fake_id");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 12541bb..a30d6a7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -135,8 +135,8 @@
                 .when(mInfoMediaManager)
                 .getRoutingSessionsForPackage();
 
-        mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1));
-        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
+        mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1, /* item */ null));
+        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, /* item */ null);
         mLocalMediaManager =
                 new LocalMediaManager(
                         mContext, mLocalBluetoothManager, mInfoMediaManager, TEST_PACKAGE_NAME);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 098ab16..3d16d6f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -171,17 +171,17 @@
 
         mBluetoothMediaDevice1 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice1, mBluetoothRouteInfo1);
+                        mContext, mCachedDevice1, mBluetoothRouteInfo1, /* item */ null);
         mBluetoothMediaDevice2 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice2, mBluetoothRouteInfo2);
+                        mContext, mCachedDevice2, mBluetoothRouteInfo2, /* item */ null);
         mBluetoothMediaDevice3 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice3, mBluetoothRouteInfo3);
-        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
-        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
-        mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
-        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo);
+                        mContext, mCachedDevice3, mBluetoothRouteInfo3, /* item */ null);
+        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1, /* item */ null);
+        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2, /* item */ null);
+        mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3, /* item */ null);
+        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mPhoneRouteInfo, /* item */ null);
     }
 
     @Test
@@ -316,7 +316,7 @@
         when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
 
         final PhoneMediaDevice phoneMediaDevice =
-                new PhoneMediaDevice(mContext, phoneRouteInfo);
+                new PhoneMediaDevice(mContext, phoneRouteInfo, /* item */ null);
 
         mMediaDevices.add(mBluetoothMediaDevice1);
         mMediaDevices.add(phoneMediaDevice);
@@ -332,7 +332,7 @@
         when(phoneRouteInfo.getType()).thenReturn(TYPE_WIRED_HEADPHONES);
 
         final PhoneMediaDevice phoneMediaDevice =
-                new PhoneMediaDevice(mContext, phoneRouteInfo);
+                new PhoneMediaDevice(mContext, phoneRouteInfo, /* item */ null);
 
         mMediaDevices.add(mInfoMediaDevice1);
         mMediaDevices.add(phoneMediaDevice);
@@ -483,7 +483,7 @@
     public void getFeatures_noRouteInfo_returnEmptyList() {
         mBluetoothMediaDevice1 =
                 new BluetoothMediaDevice(
-                        mContext, mCachedDevice1, /* MediaRoute2Info */ null);
+                        mContext, mCachedDevice1, /* MediaRoute2Info */ null, /* item */ null);
 
         assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0);
     }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 1706a6f..4125a81f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -101,7 +101,6 @@
         Settings.Global.Wearable.AMBIENT_TILT_TO_WAKE,
         Settings.Global.Wearable.AMBIENT_TOUCH_TO_WAKE,
         Settings.Global.Wearable.GESTURE_TOUCH_AND_HOLD_WATCH_FACE_ENABLED,
-        Settings.Global.Wearable.BATTERY_SAVER_MODE,
         Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_MS,
         Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER,
         Settings.Global.Wearable.DYNAMIC_COLOR_THEME_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 11fa8f4..00fb7a1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -79,6 +79,8 @@
                 Settings.System.SIP_CALL_OPTIONS,
                 Settings.System.SIP_RECEIVE_CALLS,
                 Settings.System.POINTER_SPEED,
+                Settings.System.POINTER_FILL_STYLE,
+                Settings.System.POINTER_SCALE,
                 Settings.System.VIBRATE_ON,
                 Settings.System.VIBRATE_WHEN_RINGING,
                 Settings.System.RINGTONE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 011b42f..4235bc4 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -26,6 +26,10 @@
 import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR;
+import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
+import static android.view.PointerIcon.LARGE_POINTER_SCALE;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BEGIN;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_END;
 
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -206,6 +210,11 @@
         VALIDATORS.put(System.SIP_ADDRESS_ONLY, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.SIP_ASK_ME_EACH_TIME, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.POINTER_SPEED, new InclusiveFloatRangeValidator(-7, 7));
+        VALIDATORS.put(System.POINTER_FILL_STYLE,
+                new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_FILL_BEGIN,
+                        POINTER_ICON_VECTOR_STYLE_FILL_END));
+        VALIDATORS.put(System.POINTER_SCALE,
+                new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE));
         VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
         VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java
new file mode 100644
index 0000000..5c5ca46
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Contains the list of prefixes for namespaces in which nothing can be written by background
+ * user.
+ *
+ * <p>
+ * The list in enforced is Auto devices only. To add to
+ * the list, create a change and tag the OWNER. In the change description, include a
+ * description of the flag's functionality, and a justification for why it needs to be
+ * denylisted.
+ */
+final class NonWritableNamespacesForBackgroundUserPrefixes {
+    public static final Set<String> DENYLIST =
+            new ArraySet<String>(Arrays.asList(
+                    "game_overlay"
+            ));
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 70ce202..384cb7e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2911,6 +2911,14 @@
         // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
         p.end(notificationToken);
 
+        final long pointerToken = p.start(SystemSettingsProto.POINTER);
+        dumpSetting(s, p,
+                Settings.System.POINTER_FILL_STYLE,
+                SystemSettingsProto.Pointer.POINTER_FILL_STYLE);
+        dumpSetting(s, p,
+                Settings.System.POINTER_SCALE,
+                SystemSettingsProto.Pointer.POINTER_SCALE);
+        p.end(pointerToken);
         dumpSetting(s, p,
                 Settings.System.POINTER_SPEED,
                 SystemSettingsProto.POINTER_SPEED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 15f8a7b..d54236e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2392,8 +2392,12 @@
                 == PackageManager.PERMISSION_GRANTED;
         boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID;
 
-        if (isRoot || hasWritePermission) {
+        if (isRoot) {
             return;
+        }
+
+        if (hasWritePermission) {
+            assertCallingUserDenyList(flags);
         } else if (hasAllowlistPermission) {
             for (String flag : flags) {
                 boolean namespaceAllowed = false;
@@ -2410,12 +2414,49 @@
                         + "'; allowlist permission granted, but must add flag to the allowlist.");
                 }
             }
+            assertCallingUserDenyList(flags);
         } else {
             throw new SecurityException("Permission denial to mutate flag, must have root, "
                 + "WRITE_DEVICE_CONFIG, or WRITE_ALLOWLISTED_DEVICE_CONFIG");
         }
     }
 
+    // The check is added mainly for auto devices. On auto devices, it is possible that
+    // multiple users are visible simultaneously using visible background users.
+    // In such cases, it is desired that Non-current user (ex. visible background users) can
+    // only change settings for certain namespaces.
+    private void assertCallingUserDenyList(@NonNull Set<String> flags) {
+        if (!UserManager.isVisibleBackgroundUsersEnabled()) {
+            // enforce the deny list only on devices supporting visible background user.
+            return;
+        }
+
+        int callingUser = UserHandle.getCallingUserId();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            int currentUser = ActivityManager.getCurrentUser();
+            if (callingUser == currentUser) {
+                // enforce the deny list only if the caller is not current user. Currently only auto
+                // uses background visible user, and auto doesn't support profiles so profiles of
+                // current users is not checked here.
+                return;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        for (String flag : flags) {
+            for (String denylistedPrefix :
+                    NonWritableNamespacesForBackgroundUserPrefixes.DENYLIST) {
+                if (flag.startsWith(denylistedPrefix)) {
+                    throw new SecurityException("Permission denial for flag '" + flag
+                            + "' for background user " + callingUser + ". Namespace is added to "
+                            + "denylist.");
+                }
+            }
+        }
+    }
+
     private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk(
             int targetSdkVersion, String name) {
         // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index c8992c3..05eb044 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -18,6 +18,7 @@
 
 import static android.os.Process.FIRST_APPLICATION_UID;
 
+import android.aconfig.Aconfig.flag_permission;
 import android.aconfig.Aconfig.flag_state;
 import android.aconfig.Aconfig.parsed_flag;
 import android.aconfig.Aconfig.parsed_flags;
@@ -370,7 +371,6 @@
         mAconfigDefaultFlags = new HashMap<>();
 
         ProtoOutputStream requests = null;
-        Map<String, AconfigdFlagInfo> aconfigFlagMap = new HashMap<>();
 
         synchronized (mLock) {
             readStateSyncLocked();
@@ -390,12 +390,12 @@
 
             if (enableAconfigStorageDaemon()) {
                 if (isConfigSettingsKey(mKey)) {
-                    aconfigFlagMap = getAllAconfigFlagsFromSettings();
+                    getAllAconfigFlagsFromSettings(mAconfigDefaultFlags);
                 }
             }
 
             if (isConfigSettingsKey(mKey)) {
-                requests = handleBulkSyncToNewStorage(aconfigFlagMap);
+                requests = handleBulkSyncToNewStorage(mAconfigDefaultFlags);
             }
         }
 
@@ -414,7 +414,6 @@
                     Map<String, AconfigdFlagInfo> aconfigdFlagMap =
                             AconfigdJavaUtils.listFlagsValueInNewStorage(localSocket);
                     compareFlagValueInNewStorage(
-                            aconfigFlagMap,
                             mAconfigDefaultFlags,
                             aconfigdFlagMap);
                 }
@@ -424,7 +423,6 @@
 
     // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
     public int compareFlagValueInNewStorage(
-            Map<String, AconfigdFlagInfo> settingFlagMap,
             Map<String, AconfigdFlagInfo> defaultFlagMap,
             Map<String, AconfigdFlagInfo> aconfigdFlagMap) {
 
@@ -434,9 +432,6 @@
         for (Map.Entry<String, AconfigdFlagInfo> entry : defaultFlagMap.entrySet()) {
             String key = entry.getKey();
             AconfigdFlagInfo flag = entry.getValue();
-            if (settingFlagMap.containsKey(key)) {
-                flag.merge(settingFlagMap.get(key));
-            }
 
             AconfigdFlagInfo aconfigdFlag = aconfigdFlagMap.get(key);
             if (aconfigdFlag == null) {
@@ -460,14 +455,31 @@
             diffNum++;
         }
 
+        String compareMarkerName = "aconfigd_marker/compare_diff_num";
+        synchronized (mLock) {
+            Setting markerSetting = mSettings.get(compareMarkerName);
+            if (markerSetting == null) {
+                markerSetting =
+                        new Setting(
+                                compareMarkerName,
+                                String.valueOf(diffNum),
+                                false,
+                                "aconfig",
+                                "aconfig");
+                mSettings.put(compareMarkerName, markerSetting);
+            }
+            markerSetting.value = String.valueOf(diffNum);
+        }
+
         if (diffNum == 0) {
-            Slog.i(LOG_TAG, "Settings and new storage have same flags.");
+            Slog.w(LOG_TAG, "Settings and new storage have same flags.");
         }
         return diffNum;
     }
 
     @GuardedBy("mLock")
-    public Map<String, AconfigdFlagInfo> getAllAconfigFlagsFromSettings() {
+    public int getAllAconfigFlagsFromSettings(
+            @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) {
         Map<String, AconfigdFlagInfo> ret = new HashMap<>();
         int numSettings = mSettings.size();
         int num_requests = 0;
@@ -475,25 +487,24 @@
             String name = mSettings.keyAt(i);
             Setting setting = mSettings.valueAt(i);
             AconfigdFlagInfo flag =
-                    getFlagOverrideToSync(name, setting.getValue());
+                    getFlagOverrideToSync(name, setting.getValue(), flagInfoDefault);
             if (flag == null) {
                 continue;
             }
-            String fullFlagName = flag.getFullFlagName();
-            AconfigdFlagInfo prev = ret.putIfAbsent(fullFlagName,flag);
-            if (prev != null) {
-                prev.merge(flag);
+            if (flag.getIsReadWrite()) {
+                ++num_requests;
             }
-            ++num_requests;
         }
         Slog.i(LOG_TAG, num_requests + " flag override requests created");
-        return ret;
+        return num_requests;
     }
 
     // TODO(b/341764371): migrate aconfig flag push to GMS core
     @VisibleForTesting
     @GuardedBy("mLock")
-    public AconfigdFlagInfo getFlagOverrideToSync(String name, String value) {
+    @Nullable
+    public AconfigdFlagInfo getFlagOverrideToSync(
+            String name, String value, @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) {
         int slashIdx = name.indexOf("/");
         if (slashIdx <= 0 || slashIdx >= name.length() - 1) {
             Slog.e(LOG_TAG, "invalid flag name " + name);
@@ -516,33 +527,23 @@
             fullFlagName = fullFlagName.substring(colonIdx + 1);
             isLocal = true;
         }
-
-        String aconfigName = namespace + "/" + fullFlagName;
-        boolean isAconfig =
-                mNamespaceDefaults.containsKey(namespace)
-                        && mNamespaceDefaults.get(namespace).containsKey(aconfigName);
-        if (!isAconfig) {
-            return null;
-        }
-
         // get package name and flag name
         int dotIdx = fullFlagName.lastIndexOf(".");
         if (dotIdx == -1) {
             Slog.e(LOG_TAG, "invalid override flag name " + name);
             return null;
         }
-
-        AconfigdFlagInfo.Builder builder = AconfigdFlagInfo.newBuilder()
-                        .setPackageName(fullFlagName.substring(0, dotIdx))
-                        .setFlagName(fullFlagName.substring(dotIdx + 1))
-                        .setDefaultFlagValue(mNamespaceDefaults.get(namespace).get(aconfigName));
+        AconfigdFlagInfo flag = flagInfoDefault.get(fullFlagName);
+        if (flag == null) {
+            return null;
+        }
 
         if (isLocal) {
-            builder.setHasLocalOverride(isLocal).setBootFlagValue(value).setLocalFlagValue(value);
+            flag.setLocalFlagValue(value);
         } else {
-            builder.setHasServerOverride(true).setServerFlagValue(value).setBootFlagValue(value);
+            flag.setServerFlagValue(value);
         }
-        return builder.build();
+        return flag;
     }
 
 
@@ -574,16 +575,28 @@
 
                 // loop over all settings and add flag override requests
                 for (AconfigdFlagInfo flag : aconfigFlagMap.values()) {
-                    String value =
-                            flag.getHasLocalOverride()
-                                    ? flag.getLocalFlagValue()
-                                    : flag.getServerFlagValue();
-                    AconfigdJavaUtils.writeFlagOverrideRequest(
-                            requests,
-                            flag.getPackageName(),
-                            flag.getFlagName(),
-                            value,
-                            flag.getHasLocalOverride());
+                    // don't sync read_only flags
+                    if (!flag.getIsReadWrite()) {
+                        continue;
+                    }
+
+                    if (flag.getHasServerOverride()) {
+                        AconfigdJavaUtils.writeFlagOverrideRequest(
+                                requests,
+                                flag.getPackageName(),
+                                flag.getFlagName(),
+                                flag.getServerFlagValue(),
+                                false);
+                    }
+
+                    if (flag.getHasLocalOverride()) {
+                        AconfigdJavaUtils.writeFlagOverrideRequest(
+                                requests,
+                                flag.getPackageName(),
+                                flag.getFlagName(),
+                                flag.getLocalFlagValue(),
+                                true);
+                    }
                 }
 
                 // mark sync has been done
@@ -669,6 +682,7 @@
                 String fullFlagName = flag.getPackage() + "." + flag.getName();
                 String flagName = flag.getNamespace() + "/" + fullFlagName;
                 String flagValue = flag.getState() == flag_state.ENABLED ? "true" : "false";
+                boolean isReadWrite = flag.getPermission() == flag_permission.READ_WRITE;
                 defaultMap.get(flag.getNamespace()).put(flagName, flagValue);
                 if (!flagInfoDefault.containsKey(fullFlagName)) {
                     flagInfoDefault.put(
@@ -677,6 +691,7 @@
                                     .setPackageName(flag.getPackage())
                                     .setFlagName(flag.getName())
                                     .setDefaultFlagValue(flagValue)
+                                    .setIsReadWrite(isReadWrite)
                                     .build());
                 }
             }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 4ec170d..5f8e933 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -595,6 +595,7 @@
                     Settings.Global.Wearable.MOBILE_SIGNAL_DETECTOR,
                     Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED_DEV,
                     Settings.Global.Wearable.AMBIENT_TILT_TO_BRIGHT,
+                    Settings.Global.Wearable.BATTERY_SAVER_MODE,
                     Settings.Global.Wearable.DECOMPOSABLE_WATCHFACE,
                     Settings.Global.Wearable.AMBIENT_FORCE_WHEN_DOCKED,
                     Settings.Global.Wearable.AMBIENT_LOW_BIT_ENABLED,
@@ -678,6 +679,7 @@
                  Settings.Secure.COMPLETED_CATEGORY_PREFIX,
                  Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
                  Settings.Secure.CONTENT_CAPTURE_ENABLED,
+                 Settings.Secure.CONTEXTUAL_SEARCH_PACKAGE,
                  Settings.Secure.DEFAULT_INPUT_METHOD,
                  Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD,
                  Settings.Secure.DEVICE_PAIRED,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 256b999..94aeb9b 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -143,18 +143,20 @@
                         .setDescription("another test flag")
                         .addBug("12345678")
                         .setState(Aconfig.flag_state.ENABLED)
-                        .setPermission(Aconfig.flag_permission.READ_WRITE))
+                        .setPermission(Aconfig.flag_permission.READ_ONLY))
                 .build();
-        
+
         AconfigdFlagInfo flag1 = AconfigdFlagInfo.newBuilder()
                                                 .setPackageName("com.android.flags")
                                                 .setFlagName("flag1")
                                                 .setDefaultFlagValue("false")
+                                                .setIsReadWrite(true)
                                                 .build();
         AconfigdFlagInfo flag2 = AconfigdFlagInfo.newBuilder()
                                                 .setPackageName("com.android.flags")
                                                 .setFlagName("flag2")
                                                 .setDefaultFlagValue("true")
+                                                .setIsReadWrite(false)
                                                 .build();
         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
 
@@ -984,66 +986,62 @@
     }
 
     @Test
-    public void testGetFlagOverrideToSync() {
+   public void testGetFlagOverrideToSync() {
         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
         Object lock = new Object();
-        SettingsState settingsState = new SettingsState(
-                InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
-                SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
-        parsed_flags flags = parsed_flags
-                .newBuilder()
-                .addParsedFlag(parsed_flag
-                    .newBuilder()
-                        .setPackage("com.android.flags")
-                        .setName("flag1")
-                        .setNamespace("test_namespace")
-                        .setDescription("test flag")
-                        .addBug("12345678")
-                        .setState(Aconfig.flag_state.DISABLED)
-                        .setPermission(Aconfig.flag_permission.READ_WRITE))
-                .build();
+        SettingsState settingsState =
+                new SettingsState(
+                        InstrumentationRegistry.getContext(),
+                        lock,
+                        mSettingsFile,
+                        configKey,
+                        SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED,
+                        Looper.getMainLooper());
         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
 
-        synchronized (lock) {
-            Map<String, Map<String, String>> defaults = new HashMap<>();
-            settingsState.loadAconfigDefaultValues(
-                flags.toByteArray(), defaults, flagInfoDefault);
-            Map<String, String> namespaceDefaults = defaults.get("test_namespace");
-            assertEquals(1, namespaceDefaults.keySet().size());
-            settingsState.addAconfigDefaultValuesFromMap(defaults);
-        }
-
         // invalid flag name
-        assertTrue(settingsState.getFlagOverrideToSync(
-            "invalid_flag", "false") == null);
+        assertNull(settingsState.getFlagOverrideToSync("invalid_flag", "false", flagInfoDefault));
 
-        // non aconfig flag
-        assertTrue(settingsState.getFlagOverrideToSync(
-            "some_namespace/some_flag", "false") == null);
+        // invalid local override flag name
+        assertNull(
+                settingsState.getFlagOverrideToSync(
+                        "some_namespace/some_flag", "false", flagInfoDefault));
+
+        // not a aconfig flag
+        assertNull(
+                settingsState.getFlagOverrideToSync(
+                        "some_namespace/some_flag.com", "false", flagInfoDefault));
+
+        AconfigdFlagInfo flag1 =
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag1")
+                        .setDefaultFlagValue("false")
+                        .setIsReadWrite(true)
+                        .build();
+
+        flagInfoDefault.put(flag1.getFullFlagName(), flag1);
 
         // server override
-        AconfigdFlagInfo flag = settingsState.getFlagOverrideToSync(
-            "test_namespace/com.android.flags.flag1", "false");
-        assertTrue(flag != null);
-        assertEquals(flag.getPackageName(), "com.android.flags");
-        assertEquals(flag.getFlagName(), "flag1");
-        assertEquals("false", flag.getBootFlagValue());
-        assertEquals("false", flag.getServerFlagValue());
-        assertFalse(flag.getHasLocalOverride());
-        assertNull(flag.getLocalFlagValue());
-        assertEquals("false", flag.getDefaultFlagValue());
+
+        settingsState.getFlagOverrideToSync(
+                "test_namespace/com.android.flags.flag1", "true", flagInfoDefault);
+        assertEquals("com.android.flags", flag1.getPackageName());
+        assertEquals("flag1", flag1.getFlagName());
+        assertEquals("true", flag1.getBootFlagValue());
+        assertEquals("true", flag1.getServerFlagValue());
+        assertEquals("false", flag1.getDefaultFlagValue());
+        assertTrue(flag1.getHasServerOverride());
+        assertNull(flag1.getLocalFlagValue());
 
         // local override
-        flag = settingsState.getFlagOverrideToSync(
-            "device_config_overrides/test_namespace:com.android.flags.flag1", "false");
-        assertTrue(flag != null);
-        assertEquals(flag.getPackageName(), "com.android.flags");
-        assertEquals(flag.getFlagName(), "flag1");
-        assertEquals("false", flag.getLocalFlagValue());
-        assertEquals("false", flag.getBootFlagValue());
-        assertTrue(flag.getHasLocalOverride());
-        assertNull(flag.getServerFlagValue());
-        assertEquals("false", flag.getDefaultFlagValue());
+        settingsState.getFlagOverrideToSync(
+                "device_config_overrides/test_namespace:com.android.flags.flag1",
+                "false",
+                flagInfoDefault);
+        assertEquals("false", flag1.getBootFlagValue());
+        assertEquals("false", flag1.getLocalFlagValue());
+        assertTrue(flag1.getHasLocalOverride());
     }
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -1053,20 +1051,37 @@
     public void testHandleBulkSyncWithAconfigdEnabled() {
         int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
         Object lock = new Object();
-        SettingsState settingsState = new SettingsState(
-                InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
-                SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+        SettingsState settingsState =
+                new SettingsState(
+                        InstrumentationRegistry.getContext(),
+                        lock,
+                        mSettingsFile,
+                        configKey,
+                        SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED,
+                        Looper.getMainLooper());
 
         Map<String, AconfigdFlagInfo> flags = new HashMap<>();
-        AconfigdFlagInfo flag = AconfigdFlagInfo.newBuilder()
-        .setPackageName("com.android.flags")
-                .setFlagName("flag1")
-                .setBootFlagValue("true").build();
-        flags.put("com.android.flags/flag1", flag);
+        flags.put(
+                "com.android.flags/flag1",
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag1")
+                        .setBootFlagValue("true")
+                        .setIsReadWrite(true)
+                        .build());
+
+        flags.put(
+                "com.android.flags/flag2",
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag2")
+                        .setBootFlagValue("true")
+                        .setIsReadWrite(false)
+                        .build());
 
         synchronized (lock) {
-            settingsState.insertSettingLocked("aconfigd_marker/bulk_synced",
-                    "false", null, false, "aconfig");
+            settingsState.insertSettingLocked(
+                    "aconfigd_marker/bulk_synced", "false", null, false, "aconfig");
 
             // first bulk sync
             ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
@@ -1125,6 +1140,8 @@
                             + "value=\"true\" package=\"com.android.flags\" />"
                         + "  <setting id=\"3\" name=\"test_namespace/com.android.flags.flag3\" "
                             + "value=\"true\" package=\"com.android.flags\" />"
+                        + "  <setting id=\"3\" name=\"device_config_overrides/test_namespace:com.android.flags.flag3\" "
+                            + "value=\"true\" package=\"com.android.flags\" />"
                         + "</settings>");
         os.close();
 
@@ -1134,97 +1151,76 @@
                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
 
-        Map<String, AconfigdFlagInfo> ret;
-        synchronized (lock) {
-            ret = settingsState.getAllAconfigFlagsFromSettings();
-        }
-
-        assertTrue(ret.isEmpty());
-
-        parsed_flags flags =
-                parsed_flags
-                        .newBuilder()
-                        .addParsedFlag(
-                                parsed_flag
-                                        .newBuilder()
-                                        .setPackage("com.android.flags")
-                                        .setName("flag1")
-                                        .setNamespace("test_namespace")
-                                        .setDescription("test flag")
-                                        .addBug("12345678")
-                                        .setState(Aconfig.flag_state.DISABLED)
-                                        .setPermission(Aconfig.flag_permission.READ_WRITE))
-                        .addParsedFlag(
-                                parsed_flag
-                                        .newBuilder()
-                                        .setPackage("com.android.flags")
-                                        .setName("flag2")
-                                        .setNamespace("test_namespace")
-                                        .setDescription("test flag")
-                                        .addBug("12345678")
-                                        .setState(Aconfig.flag_state.DISABLED)
-                                        .setPermission(Aconfig.flag_permission.READ_WRITE))
-                        .addParsedFlag(
-                                parsed_flag
-                                        .newBuilder()
-                                        .setPackage("com.android.flags")
-                                        .setName("flag3")
-                                        .setNamespace("test_namespace")
-                                        .setDescription("test flag")
-                                        .addBug("12345678")
-                                        .setState(Aconfig.flag_state.DISABLED)
-                                        .setPermission(Aconfig.flag_permission.READ_WRITE))
-                        .build();
-
-        Map<String, Map<String, String>> defaults = new HashMap<>();
+        int ret;
         Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
         synchronized (lock) {
-            settingsState.loadAconfigDefaultValues(
-                flags.toByteArray(), defaults, flagInfoDefault);
-            settingsState.addAconfigDefaultValuesFromMap(defaults);
-            ret = settingsState.getAllAconfigFlagsFromSettings();
+            ret = settingsState.getAllAconfigFlagsFromSettings(flagInfoDefault);
         }
+        assertEquals(0, ret);
 
-        AconfigdFlagInfo expectedFlag1 =
+        AconfigdFlagInfo flag1 =
                 AconfigdFlagInfo.newBuilder()
                         .setPackageName("com.android.flags")
                         .setFlagName("flag1")
-                        .setServerFlagValue("false")
-                        .setLocalFlagValue("true")
                         .setDefaultFlagValue("false")
-                        .setBootFlagValue("true")
-                        .setHasServerOverride(true)
-                        .setHasLocalOverride(true)
-                        .setIsReadWrite(false)
+                        .setIsReadWrite(true)
                         .build();
+        flagInfoDefault.put(flag1.getFullFlagName(), flag1);
 
-        AconfigdFlagInfo expectedFlag2 =
+        synchronized (lock) {
+            ret = settingsState.getAllAconfigFlagsFromSettings(flagInfoDefault);
+        }
+        assertEquals(2, ret);
+        assertEquals("com.android.flags", flag1.getPackageName());
+        assertEquals("flag1", flag1.getFlagName());
+        assertEquals("true", flag1.getBootFlagValue());
+        assertEquals("true", flag1.getLocalFlagValue());
+        assertEquals("false", flag1.getServerFlagValue());
+        assertEquals("false", flag1.getDefaultFlagValue());
+        assertTrue(flag1.getHasServerOverride());
+        assertTrue(flag1.getHasLocalOverride());
+
+        AconfigdFlagInfo flag2 =
                 AconfigdFlagInfo.newBuilder()
                         .setPackageName("com.android.flags")
                         .setFlagName("flag2")
-                        .setLocalFlagValue("true")
                         .setDefaultFlagValue("false")
-                        .setBootFlagValue("true")
-                        .setHasLocalOverride(true)
-                        .setHasServerOverride(false)
-                        .setIsReadWrite(false)
+                        .setIsReadWrite(true)
                         .build();
+        flagInfoDefault.put(flag2.getFullFlagName(), flag2);
+        synchronized (lock) {
+            ret = settingsState.getAllAconfigFlagsFromSettings(flagInfoDefault);
+        }
+        assertEquals(3, ret);
+        assertEquals("com.android.flags", flag2.getPackageName());
+        assertEquals("flag2", flag2.getFlagName());
+        assertEquals("true", flag2.getBootFlagValue());
+        assertEquals("true", flag2.getLocalFlagValue());
+        assertEquals("false", flag2.getDefaultFlagValue());
+        assertNull(flag2.getServerFlagValue());
+        assertFalse(flag2.getHasServerOverride());
+        assertTrue(flag2.getHasLocalOverride());
 
-
-        AconfigdFlagInfo expectedFlag3 =
+        AconfigdFlagInfo flag3 =
                 AconfigdFlagInfo.newBuilder()
                         .setPackageName("com.android.flags")
                         .setFlagName("flag3")
-                        .setServerFlagValue("true")
-                        .setBootFlagValue("true")
                         .setDefaultFlagValue("false")
-                        .setHasServerOverride(true)
                         .setIsReadWrite(false)
                         .build();
-
-        assertEquals(expectedFlag1, ret.get("com.android.flags.flag1"));
-        assertEquals(expectedFlag2, ret.get("com.android.flags.flag2"));
-        assertEquals(expectedFlag3, ret.get("com.android.flags.flag3"));
+        flagInfoDefault.put(flag3.getFullFlagName(), flag3);
+        synchronized (lock) {
+            ret = settingsState.getAllAconfigFlagsFromSettings(flagInfoDefault);
+        }
+        assertEquals(3, ret);
+        assertEquals("com.android.flags", flag3.getPackageName());
+        assertEquals("flag3", flag3.getFlagName());
+        assertEquals("false", flag3.getBootFlagValue());
+        assertEquals("false", flag3.getDefaultFlagValue());
+        assertNull(flag3.getLocalFlagValue());
+        assertNull(flag3.getServerFlagValue());
+        assertFalse(flag3.getHasServerOverride());
+        assertFalse(flag3.getHasLocalOverride());
     }
 
     @Test
@@ -1245,37 +1241,34 @@
                         .setPackageName("com.android.flags")
                         .setFlagName("flag1")
                         .setDefaultFlagValue("false")
-                        .build();
-
-        AconfigdFlagInfo settingFlag1 =
-                AconfigdFlagInfo.newBuilder()
-                        .setPackageName("com.android.flags")
-                        .setFlagName("flag1")
                         .setServerFlagValue("true")
                         .setHasServerOverride(true)
+                        .setIsReadWrite(true)
                         .build();
 
         AconfigdFlagInfo expectedFlag1 =
                 AconfigdFlagInfo.newBuilder()
                         .setPackageName("com.android.flags")
                         .setFlagName("flag1")
-                        .setBootFlagValue("true")
                         .setServerFlagValue("true")
                         .setDefaultFlagValue("false")
                         .setHasServerOverride(true)
+                        .setIsReadWrite(true)
                         .build();
 
-        Map<String, AconfigdFlagInfo> settingMap = new HashMap<>();
         Map<String, AconfigdFlagInfo> aconfigdMap = new HashMap<>();
         Map<String, AconfigdFlagInfo> defaultMap = new HashMap<>();
 
         defaultMap.put("com.android.flags.flag1", defaultFlag1);
-        settingMap.put("com.android.flags.flag1", settingFlag1);
         aconfigdMap.put("com.android.flags.flag1", expectedFlag1);
 
-        int ret = settingsState.compareFlagValueInNewStorage(settingMap, defaultMap, aconfigdMap);
+        int ret = settingsState.compareFlagValueInNewStorage(defaultMap, aconfigdMap);
         assertEquals(0, ret);
 
+        String value =
+                settingsState.getSettingLocked("aconfigd_marker/compare_diff_num").getValue();
+        assertEquals("0", value);
+
         AconfigdFlagInfo defaultFlag2 =
                 AconfigdFlagInfo.newBuilder()
                         .setPackageName("com.android.flags")
@@ -1284,7 +1277,29 @@
                         .build();
         defaultMap.put("com.android.flags.flag2", defaultFlag2);
 
-        ret = settingsState.compareFlagValueInNewStorage(settingMap, defaultMap, aconfigdMap);
+        ret = settingsState.compareFlagValueInNewStorage(defaultMap, aconfigdMap);
+        // missing from new storage
         assertEquals(1, ret);
+        value =
+                settingsState.getSettingLocked("aconfigd_marker/compare_diff_num").getValue();
+        assertEquals("1", value);
+
+        AconfigdFlagInfo expectedFlag2 =
+        AconfigdFlagInfo.newBuilder()
+                .setPackageName("com.android.flags")
+                .setFlagName("flag2")
+                .setServerFlagValue("true")
+                .setLocalFlagValue("true")
+                .setDefaultFlagValue("false")
+                .setHasServerOverride(true)
+                .setHasLocalOverride(true)
+                .build();
+        aconfigdMap.put("com.android.flags.flag2", expectedFlag2);
+        ret = settingsState.compareFlagValueInNewStorage(defaultMap, aconfigdMap);
+        // skip the server and local value comparison when the flag is read_only
+        assertEquals(0, ret);
+        value =
+                settingsState.getSettingLocked("aconfigd_marker/compare_diff_num").getValue();
+        assertEquals("0", value);
     }
 }
diff --git a/packages/SoundPicker/res/values-vi/strings.xml b/packages/SoundPicker/res/values-vi/strings.xml
index bed0e96..b6f8793 100644
--- a/packages/SoundPicker/res/values-vi/strings.xml
+++ b/packages/SoundPicker/res/values-vi/strings.xml
@@ -20,7 +20,7 @@
     <string name="notification_sound_default" msgid="8133121186242636840">"Âm thanh thông báo mặc định"</string>
     <string name="alarm_sound_default" msgid="4787646764557462649">"Âm thanh chuông báo mặc định"</string>
     <string name="add_ringtone_text" msgid="6642389991738337529">"Thêm nhạc chuông"</string>
-    <string name="add_alarm_text" msgid="3545497316166999225">"Thêm báo thức"</string>
+    <string name="add_alarm_text" msgid="3545497316166999225">"Thêm chuông báo"</string>
     <string name="add_notification_text" msgid="4431129543300614788">"Thêm thông báo"</string>
     <string name="delete_ringtone_text" msgid="201443984070732499">"Xóa"</string>
     <string name="unable_to_add_ringtone" msgid="4583511263449467326">"Không thể thêm nhạc chuông tùy chỉnh"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 07a00fb..fde7c2c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,10 +78,50 @@
     visibility: ["//visibility:private"],
 }
 
+// Tests where robolectric conversion caused errors in SystemUITests at runtime
+filegroup {
+    name: "SystemUI-tests-broken-robofiles-sysui-run",
+    srcs: [
+        "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
+        "tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
+        "tests/src/**/systemui/doze/DozeMachineTest.java",
+        "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
+        "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
+        "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
+        "tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
+        "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt",
+    ],
+}
+
 // Tests where robolectric failed at runtime. (go/multivalent-tests)
 filegroup {
     name: "SystemUI-tests-broken-robofiles-run",
     srcs: [
+        "tests/src/**/systemui/accessibility/AccessibilityButtonModeObserverTest.java",
+        "tests/src/**/systemui/accessibility/AccessibilityButtonTargetsObserverTest.java",
+        "tests/src/**/systemui/accessibility/FullscreenMagnificationControllerTest.java",
+        "tests/src/**/systemui/accessibility/WindowMagnificationAnimationControllerTest.java",
+        "tests/src/**/systemui/animation/FontInterpolatorTest.kt",
+        "tests/src/**/systemui/animation/TextAnimatorTest.kt",
+        "tests/src/**/systemui/animation/TextInterpolatorTest.kt",
+        "tests/src/**/systemui/animation/ActivityTransitionAnimatorTest.kt",
+        "tests/src/**/systemui/animation/AnimatorTestRuleOrderTest.kt",
+        "tests/src/**/systemui/animation/DialogTransitionAnimatorTest.kt",
+        "tests/src/**/systemui/broadcast/ActionReceiverTest.kt",
+        "tests/src/**/systemui/broadcast/BroadcastDispatcherTest.kt",
+        "tests/src/**/systemui/compose/ComposeInitializerTest.kt",
+        "tests/src/**/systemui/controls/ui/ControlsActivityTest.kt",
+        "tests/src/**/systemui/controls/management/ControlsEditingActivityTest.kt",
+        "tests/src/**/systemui/controls/management/ControlsRequestDialogTest.kt",
+        "tests/src/**/systemui/controls/ui/DetailDialogTest.kt",
+        "tests/src/**/systemui/doze/DozeMachineTest.kt",
+        "tests/src/**/systemui/fontscaling/FontScalingDialogDelegateTest.kt",
+        "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
         "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java",
         "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
         "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
@@ -176,9 +216,7 @@
     ],
 }
 
-// We are running robolectric tests in the tests directory as well as
-// multivalent tests.  If you add a test, and it doesn't run in robolectric,
-// it should be added to this exclusion list. go/multivalent-tests
+// Tests where robolectric failed at compile time. (go/multivalent-tests)
 filegroup {
     name: "SystemUI-tests-broken-robofiles-compile",
     srcs: [
@@ -330,6 +368,7 @@
         "tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
         "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java",
         "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt",
+        "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt",
         "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt",
         "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt",
         "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt",
@@ -483,6 +522,7 @@
         "androidx.compose.material_material-icons-extended",
         "androidx.activity_activity-compose",
         "androidx.compose.animation_animation-graphics",
+        "androidx.lifecycle_lifecycle-viewmodel-compose",
         "device_policy_aconfig_flags_lib",
     ],
     libs: [
@@ -644,6 +684,7 @@
         "androidx.compose.material_material-icons-extended",
         "androidx.activity_activity-compose",
         "androidx.compose.animation_animation-graphics",
+        "androidx.lifecycle_lifecycle-viewmodel-compose",
         "TraceurCommon",
     ],
 }
@@ -809,6 +850,7 @@
     exclude_srcs: [
         ":SystemUI-tests-broken-robofiles-compile",
         ":SystemUI-tests-broken-robofiles-run",
+        ":SystemUI-tests-broken-robofiles-sysui-run",
     ],
     static_libs: [
         "RoboTestLibraries",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9c58371..b37db16 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -387,7 +387,7 @@
         android:killAfterRestore="false"
         android:hardwareAccelerated="true"
         android:label="@string/app_label"
-        android:icon="@drawable/android14_patch_adaptive"
+        android:icon="@drawable/android15_patch_adaptive"
         android:process="com.android.systemui"
         android:supportsRtl="true"
         android:theme="@style/Theme.SystemUI"
@@ -475,6 +475,15 @@
             android:exported="false"
             android:noHistory="true" />
 
+        <activity android:name=".touchpad.tutorial.ui.view.TouchpadTutorialActivity"
+            android:exported="true"
+            android:theme="@style/Theme.AppCompat.NoActionBar">
+            <intent-filter>
+                <action android:name="com.android.systemui.action.TOUCHPAD_TUTORIAL"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
         <service android:name=".screenshot.appclips.AppClipsScreenshotHelperService"
             android:exported="false"
             android:singleUser="true"
@@ -641,6 +650,7 @@
         <!-- started from MediaProjectionManager -->
         <activity
             android:name=".mediaprojection.permission.MediaProjectionPermissionActivity"
+            android:showForAllUsers="true"
             android:exported="true"
             android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
             android:finishOnCloseSystemDialogs="true"
@@ -651,6 +661,7 @@
         <activity
             android:name=".mediaprojection.appselector.MediaProjectionAppSelectorActivity"
             android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector"
+            android:showForAllUsers="true"
             android:finishOnCloseSystemDialogs="true"
             android:excludeFromRecents="true"
             android:documentLaunchMode="never"
@@ -1098,8 +1109,8 @@
 
         <activity android:name="com.android.systemui.keyboard.shortcut.ui.view.ShortcutHelperActivity"
             android:exported="false"
+            android:showForAllUsers="true"
             android:theme="@style/ShortcutHelperTheme"
-            android:excludeFromRecents="true"
-            android:finishOnCloseSystemDialogs="true" />
+            android:excludeFromRecents="true" />
     </application>
 </manifest>
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index d2e5a13..e03ac3d 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -11,6 +11,7 @@
 alexflo@google.com
 andonian@google.com
 amiko@google.com
+austindelgado@google.com
 aroederer@google.com
 arteiro@google.com
 asc@google.com
@@ -29,11 +30,13 @@
 cinek@google.com
 cocod@google.com
 darrellshi@google.com
+diyab@google.com
 dupin@google.com
 ethibodeau@google.com
 evanlaird@google.com
 florenceyang@google.com
 gallmann@google.com
+graciecheng@google.com
 gwasserman@google.com
 hwwang@google.com
 hyunyoungs@google.com
@@ -42,10 +45,12 @@
 jbolinger@google.com
 jdemeulenaere@google.com
 jeffdq@google.com
+jeffpu@google.com
 jernej@google.com
 jglazier@google.com
 jjaggi@google.com
 jonmiranda@google.com
+joshmccloskey@google.com
 joshtrask@google.com
 juansmartinez@google.com
 juliacr@google.com
@@ -87,6 +92,7 @@
 santie@google.com
 shanh@google.com
 snoeberger@google.com
+spdonghao@google.com
 steell@google.com
 stevenckng@google.com
 stwu@google.com
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-hr/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-hr/strings.xml
index 5c3c99c..36fcfdc 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-hr/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-hr/strings.xml
@@ -21,7 +21,7 @@
     <string name="previous_button_content_description" msgid="840869171117765966">"Idi na prethodni zaslon"</string>
     <string name="next_button_content_description" msgid="6810058269847364406">"Idi na sljedeći zaslon"</string>
     <string name="accessibility_menu_description" msgid="4458354794093858297">"Izbornik pristupačnosti veliki je zaslonski izbornik koji vam omogućuje upravljanje uređajem. Putem njega možete zaključati uređaj, upravljati glasnoćom i svjetlinom, izrađivati snimke zaslona i drugo."</string>
-    <string name="accessibility_menu_summary" msgid="340071398148208130">"Upravljanje uređajem pomoću velikog izbornika"</string>
+    <string name="accessibility_menu_summary" msgid="340071398148208130">"Upravljajte uređajem pomoću velikog izbornika"</string>
     <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Postavke izbornika pristupačnosti"</string>
     <string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Veliki gumbi"</string>
     <string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Povećanje veličine gumba izbornika Pristupačnosti"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
index 160d310..f12278a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml
@@ -2,7 +2,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string>
-    <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string>
+    <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string>
     <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string>
     <string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string>
     <string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
index 160d310..f12278a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml
@@ -2,7 +2,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string>
-    <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string>
+    <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string>
     <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string>
     <string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string>
     <string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
index f5baae2..dd149ba 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java
@@ -91,25 +91,29 @@
 
     private final DisplayManager.DisplayListener mDisplayListener =
             new DisplayManager.DisplayListener() {
-        int mRotation;
+                int mRotation;
 
-        @Override
-        public void onDisplayAdded(int displayId) {}
+                @Override
+                public void onDisplayAdded(int displayId) {
+                }
 
-        @Override
-        public void onDisplayRemoved(int displayId) {
-            // TODO(b/136716947): Need to reset A11yMenuOverlayLayout by display id.
-        }
+                @Override
+                public void onDisplayRemoved(int displayId) {
+                    // TODO(b/136716947): Need to reset A11yMenuOverlayLayout by display id.
+                }
 
-        @Override
-        public void onDisplayChanged(int displayId) {
-            Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
-            if (mRotation != display.getRotation()) {
-                mRotation = display.getRotation();
-                mA11yMenuLayout.updateViewLayout();
-            }
-        }
-    };
+                @Override
+                public void onDisplayChanged(int displayId) {
+                    if (mA11yMenuLayout == null) {
+                        return;
+                    }
+                    Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+                    if (mRotation != display.getRotation()) {
+                        mRotation = display.getRotation();
+                        mA11yMenuLayout.updateViewLayout();
+                    }
+                }
+            };
 
     private final BroadcastReceiver mHideMenuReceiver = new BroadcastReceiver() {
         @Override
@@ -373,6 +377,7 @@
     public boolean onUnbind(Intent intent) {
         unregisterReceiver(mHideMenuReceiver);
         unregisterReceiver(mToggleMenuReceiver);
+        mDisplayManager.unregisterDisplayListener(mDisplayListener);
         mPrefs.unregisterOnSharedPreferenceChangeListener(mSharedPreferenceChangeListener);
         sInitialized = false;
         if (mA11yMenuLayout != null) {
diff --git a/packages/SystemUI/aconfig/communal.aconfig b/packages/SystemUI/aconfig/communal.aconfig
index 2e9af7e..afcd8a9 100644
--- a/packages/SystemUI/aconfig/communal.aconfig
+++ b/packages/SystemUI/aconfig/communal.aconfig
@@ -7,3 +7,13 @@
     description: "Enables the communal hub experience"
     bug: "304584416"
 }
+
+flag {
+    name: "enable_widget_picker_size_filter"
+    namespace: "communal"
+    description: "Enables passing a size filter to the widget picker"
+    bug: "345482907"
+   metadata {
+        purpose: PURPOSE_BUGFIX
+   }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index f84f627..0fdcc7a 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -884,16 +884,6 @@
 }
 
 flag {
-    name: "shade_collapse_activity_launch_fix"
-    namespace: "systemui"
-    description: "Avoid collapsing the shade on activity launch if it is already collapsed, as this causes a flicker."
-    bug: "331591373"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "slice_broadcast_relay_in_background"
     namespace: "systemui"
     description: "Move handling of slice broadcast relay broadcasts to background threads"
@@ -981,6 +971,16 @@
 }
 
 flag {
+  name: "media_controls_lockscreen_shade_bug_fix"
+  namespace: "systemui"
+  description: "Use ShadeInteractor for media location changes"
+  bug: "319244625"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   namespace: "systemui"
   name: "enable_view_capture_tracing"
   description: "Enables view capture tracing in System UI."
@@ -1038,6 +1038,7 @@
   bug: "343505271"
 }
 
+
 flag {
   name: "new_touchpad_gestures_tutorial"
   namespace: "systemui"
@@ -1056,6 +1057,16 @@
 }
 
 flag {
+   name: "enable_efficient_display_repository"
+   namespace: "systemui"
+   description: "Decide whether to use the new implementation of DisplayRepository that minimizes binder calls and background lock contention."
+   bug: "345472038"
+   metadata {
+     purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
   name: "notification_media_manager_background_execution"
   namespace: "systemui"
   description: "Decide whether to execute binder calls in background thread"
@@ -1064,3 +1075,23 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "dozeui_scheduling_alarms_background_execution"
+  namespace: "systemui"
+  description: "Decide whether to execute binder calls to schedule alarms in background thread"
+  bug: "330492575"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "notification_pulsing_fix"
+  namespace: "systemui"
+  description: "Allow showing new pulsing notifications when the device is already pulsing."
+  bug: "335560575"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index c329384..7c2f7fe 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -1,6 +1,6 @@
 package com.android.systemui.communal.ui.compose
 
-import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.RepeatMode
 import androidx.compose.animation.core.animateFloat
 import androidx.compose.animation.core.infiniteRepeatable
@@ -20,20 +20,18 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.composed
 import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.BlendMode
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.CommunalSwipeDetector
@@ -57,12 +55,15 @@
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.communal.ui.compose.Dimensions.SlideOffsetY
 import com.android.systemui.communal.ui.compose.extensions.allowGestures
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.util.CommunalColors
+import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
 import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource
+import kotlin.time.DurationUnit
 
 object Communal {
     object Elements {
@@ -70,6 +71,7 @@
         val Grid = ElementKey("CommunalContent")
         val LockIcon = ElementKey("CommunalLockIcon")
         val IndicationArea = ElementKey("CommunalIndicationArea")
+        val StatusBar = ElementKey("StatusBar")
     }
 }
 
@@ -77,11 +79,24 @@
     override fun matches(key: ElementKey, scene: SceneKey) = true
 }
 
+private object TransitionDuration {
+    const val BETWEEN_HUB_AND_EDIT_MODE_MS = 1000
+    const val EDIT_MODE_TO_HUB_CONTENT_MS = 167
+    const val EDIT_MODE_TO_HUB_GRID_DELAY_MS = 167
+    const val EDIT_MODE_TO_HUB_GRID_END_MS =
+        EDIT_MODE_TO_HUB_GRID_DELAY_MS + EDIT_MODE_TO_HUB_CONTENT_MS
+    const val HUB_TO_EDIT_MODE_CONTENT_MS = 250
+}
+
 val sceneTransitions = transitions {
     to(CommunalScenes.Communal, key = CommunalTransitionKeys.SimpleFade) {
         spec = tween(durationMillis = 250)
         fade(AllElements)
     }
+    to(CommunalScenes.Blank, key = CommunalTransitionKeys.SimpleFade) {
+        spec = tween(durationMillis = TO_GONE_DURATION.toInt(DurationUnit.MILLISECONDS))
+        fade(AllElements)
+    }
     to(CommunalScenes.Communal) {
         spec = tween(durationMillis = 1000)
         translate(Communal.Elements.Grid, Edge.Right)
@@ -94,9 +109,34 @@
             fade(Communal.Elements.Grid)
             fade(Communal.Elements.IndicationArea)
             fade(Communal.Elements.LockIcon)
+            fade(Communal.Elements.StatusBar)
         }
         timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
     }
+    to(CommunalScenes.Blank, key = CommunalTransitionKeys.ToEditMode) {
+        spec = tween(durationMillis = TransitionDuration.BETWEEN_HUB_AND_EDIT_MODE_MS)
+        timestampRange(endMillis = TransitionDuration.HUB_TO_EDIT_MODE_CONTENT_MS) {
+            fade(Communal.Elements.Grid)
+            fade(Communal.Elements.IndicationArea)
+            fade(Communal.Elements.LockIcon)
+        }
+        fade(Communal.Elements.Scrim)
+    }
+    to(CommunalScenes.Communal, key = CommunalTransitionKeys.FromEditMode) {
+        spec = tween(durationMillis = TransitionDuration.BETWEEN_HUB_AND_EDIT_MODE_MS)
+        translate(Communal.Elements.Grid, y = SlideOffsetY)
+        timestampRange(endMillis = TransitionDuration.EDIT_MODE_TO_HUB_CONTENT_MS) {
+            fade(Communal.Elements.IndicationArea)
+            fade(Communal.Elements.LockIcon)
+            fade(Communal.Elements.Scrim)
+        }
+        timestampRange(
+            startMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_DELAY_MS,
+            endMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_END_MS
+        ) {
+            fade(Communal.Elements.Grid)
+        }
+    }
 }
 
 /**
@@ -192,10 +232,14 @@
 
         scene(
             CommunalScenes.Communal,
-            userActions =
-                mapOf(Swipe(SwipeDirection.Right, fromSource = Edge.Left) to CommunalScenes.Blank)
+            userActions = mapOf(Swipe(SwipeDirection.Right) to CommunalScenes.Blank)
         ) {
-            CommunalScene(backgroundType, colors, content)
+            CommunalScene(
+                backgroundType = backgroundType,
+                colors = colors,
+                content = content,
+                modifier = Modifier.horizontalNestedScrollToScene(),
+            )
         }
     }
 
@@ -217,6 +261,7 @@
             CommunalBackgroundType.DEFAULT -> DefaultBackground(colors = colors)
             CommunalBackgroundType.STATIC_GRADIENT -> StaticLinearGradient()
             CommunalBackgroundType.ANIMATED -> AnimatedLinearGradient()
+            CommunalBackgroundType.NONE -> BackgroundTopScrim()
         }
     }
     with(content) { Content(modifier = modifier) }
@@ -252,7 +297,11 @@
     val colors = LocalAndroidColorScheme.current
     Box(
         Modifier.matchParentSize()
-            .animatedGradientBackground(colors = listOf(colors.primary, colors.primaryContainer))
+            .background(colors.primary)
+            .animatedRadialGradientBackground(
+                toColor = colors.primary,
+                fromColor = colors.primaryContainer.copy(alpha = 0.6f)
+            )
     )
     BackgroundTopScrim()
 }
@@ -265,29 +314,76 @@
     Box(Modifier.matchParentSize().alpha(0.34f).background(scrimOnTopColor))
 }
 
-/** Modifier which sets the background of a composable to an animated gradient */
+/** The duration to use for the gradient background animation. */
+private const val ANIMATION_DURATION_MS = 10_000
+
+/** The offset to use in order to place the center of each gradient offscreen. */
+private val ANIMATION_OFFSCREEN_OFFSET = 128.dp
+
+/** Modifier which creates two radial gradients that animate up and down. */
 @Composable
-private fun Modifier.animatedGradientBackground(colors: List<Color>): Modifier = composed {
-    var size by remember { mutableStateOf(IntSize.Zero) }
-    val transition = rememberInfiniteTransition(label = "scrim background")
-    val startOffsetX by
-        transition.animateFloat(
-            initialValue = -size.width.toFloat(),
-            targetValue = size.width.toFloat(),
+fun Modifier.animatedRadialGradientBackground(toColor: Color, fromColor: Color): Modifier {
+    val density = LocalDensity.current
+    val infiniteTransition = rememberInfiniteTransition(label = "radial gradient transition")
+    val centerFraction by
+        infiniteTransition.animateFloat(
+            initialValue = 0f,
+            targetValue = 1f,
             animationSpec =
                 infiniteRepeatable(
-                    animation = tween(durationMillis = 5_000, easing = LinearEasing),
-                    repeatMode = RepeatMode.Reverse,
+                    animation =
+                        tween(
+                            durationMillis = ANIMATION_DURATION_MS,
+                            easing = CubicBezierEasing(0.33f, 0f, 0.67f, 1f),
+                        ),
+                    repeatMode = RepeatMode.Reverse
                 ),
-            label = "scrim start offset"
+            label = "radial gradient center fraction"
         )
-    background(
+
+    // Offset to place the center of the gradients offscreen. This is applied to both the
+    // x and y coordinates.
+    val offsetPx = remember(density) { with(density) { ANIMATION_OFFSCREEN_OFFSET.toPx() } }
+
+    return drawBehind {
+        val gradientRadius = (size.width / 2) + offsetPx
+        val totalHeight = size.height + 2 * offsetPx
+
+        val leftCenter =
+            Offset(
+                x = -offsetPx,
+                y = totalHeight * centerFraction - offsetPx,
+            )
+        val rightCenter =
+            Offset(
+                x = offsetPx + size.width,
+                y = totalHeight * (1f - centerFraction) - offsetPx,
+            )
+
+        // Right gradient
+        drawCircle(
             brush =
-                Brush.linearGradient(
-                    colors = colors,
-                    start = Offset(startOffsetX, 0f),
-                    end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat()),
-                )
+                Brush.radialGradient(
+                    colors = listOf(fromColor, toColor),
+                    center = rightCenter,
+                    radius = gradientRadius
+                ),
+            center = rightCenter,
+            radius = gradientRadius,
+            blendMode = BlendMode.SrcAtop,
         )
-        .onGloballyPositioned { size = it.size }
+
+        // Left gradient
+        drawCircle(
+            brush =
+                Brush.radialGradient(
+                    colors = listOf(fromColor, toColor),
+                    center = leftCenter,
+                    radius = gradientRadius
+                ),
+            center = leftCenter,
+            radius = gradientRadius,
+            blendMode = BlendMode.SrcAtop,
+        )
+    }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 77665155..18085ab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -16,13 +16,17 @@
 
 package com.android.systemui.communal.ui.compose
 
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntRect
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
+import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
 import com.android.systemui.keyguard.ui.composable.section.LockSection
@@ -34,20 +38,27 @@
 @Inject
 constructor(
     private val viewModel: CommunalViewModel,
+    private val interactionHandler: SmartspaceInteractionHandler,
     private val dialogFactory: SystemUIDialogFactory,
     private val lockSection: LockSection,
+    private val ambientStatusBarSection: AmbientStatusBarSection,
 ) {
-
     @Composable
     fun SceneScope.Content(modifier: Modifier = Modifier) {
         Layout(
             modifier = modifier.fillMaxSize(),
             content = {
-                CommunalHub(
-                    viewModel = viewModel,
-                    dialogFactory = dialogFactory,
-                    modifier = Modifier.element(Communal.Elements.Grid)
-                )
+                Box(modifier = Modifier.fillMaxSize()) {
+                    with(ambientStatusBarSection) {
+                        AmbientStatusBar(modifier = Modifier.fillMaxWidth())
+                    }
+                    CommunalHub(
+                        viewModel = viewModel,
+                        interactionHandler = interactionHandler,
+                        dialogFactory = dialogFactory,
+                        modifier = Modifier.element(Communal.Elements.Grid)
+                    )
+                }
                 with(lockSection) {
                     LockIcon(
                         overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 1f7f07b..fbfe050 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -16,17 +16,16 @@
 
 package com.android.systemui.communal.ui.compose
 
-import android.appwidget.AppWidgetHostView
 import android.graphics.drawable.Icon
 import android.os.Bundle
 import android.util.SizeF
 import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
 import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
 import android.widget.FrameLayout
+import android.widget.RemoteViews
 import androidx.annotation.VisibleForTesting
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.AnimatedVisibilityScope
-import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.animateFloatAsState
@@ -34,6 +33,8 @@
 import androidx.compose.animation.core.tween
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Image
@@ -46,6 +47,7 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -128,6 +130,7 @@
 import androidx.compose.ui.window.Popup
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.window.layout.WindowMetricsCalculator
+import com.android.compose.animation.Easings.Emphasized
 import com.android.compose.modifiers.thenIf
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.compose.ui.graphics.painter.rememberDrawablePainter
@@ -144,6 +147,7 @@
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.PopupType
+import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
@@ -154,6 +158,7 @@
 fun CommunalHub(
     modifier: Modifier = Modifier,
     viewModel: BaseCommunalViewModel,
+    interactionHandler: RemoteViews.InteractionHandler? = null,
     dialogFactory: SystemUIDialogFactory? = null,
     widgetConfigurator: WidgetConfigurator? = null,
     onOpenWidgetPicker: (() -> Unit)? = null,
@@ -173,6 +178,10 @@
         derivedStateOf { selectedKey.value != null || reorderingWidgets }
     }
     val isEmptyState by viewModel.isEmptyState.collectAsStateWithLifecycle(initialValue = false)
+    val isCommunalContentVisible by
+        viewModel.isCommunalContentVisible.collectAsStateWithLifecycle(
+            initialValue = !viewModel.isEditMode
+        )
 
     val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
     val contentOffset = beforeContentPadding(contentPadding).toOffset()
@@ -245,46 +254,88 @@
                     viewModel = viewModel,
                 )
             } else {
-                CommunalHubLazyGrid(
-                    communalContent = communalContent,
-                    viewModel = viewModel,
-                    contentPadding = contentPadding,
-                    contentOffset = contentOffset,
-                    setGridCoordinates = { gridCoordinates = it },
-                    updateDragPositionForRemove = { offset ->
-                        isPointerWithinEnabledRemoveButton(
-                            removeEnabled = removeButtonEnabled,
-                            offset = gridCoordinates?.let { it.positionInWindow() + offset },
-                            containerToCheck = removeButtonCoordinates
+                val slideOffsetInPx =
+                    with(LocalDensity.current) { Dimensions.SlideOffsetY.toPx().toInt() }
+                AnimatedVisibility(
+                    visible = isCommunalContentVisible,
+                    enter =
+                        fadeIn(
+                            animationSpec =
+                                tween(durationMillis = 83, delayMillis = 83, easing = LinearEasing)
+                        ) +
+                            slideInVertically(
+                                animationSpec = tween(durationMillis = 1000, easing = Emphasized),
+                                initialOffsetY = { -slideOffsetInPx }
+                            ),
+                    exit =
+                        fadeOut(
+                            animationSpec = tween(durationMillis = 167, easing = LinearEasing)
+                        ) +
+                            slideOutVertically(
+                                animationSpec = tween(durationMillis = 1000, easing = Emphasized),
+                                targetOffsetY = { -slideOffsetInPx }
+                            ),
+                    modifier = Modifier.fillMaxSize(),
+                ) {
+                    Box {
+                        CommunalHubLazyGrid(
+                            communalContent = communalContent,
+                            viewModel = viewModel,
+                            contentPadding = contentPadding,
+                            contentOffset = contentOffset,
+                            setGridCoordinates = { gridCoordinates = it },
+                            updateDragPositionForRemove = { offset ->
+                                isPointerWithinEnabledRemoveButton(
+                                    removeEnabled = removeButtonEnabled,
+                                    offset =
+                                        gridCoordinates?.let { it.positionInWindow() + offset },
+                                    containerToCheck = removeButtonCoordinates
+                                )
+                            },
+                            gridState = gridState,
+                            contentListState = contentListState,
+                            selectedKey = selectedKey,
+                            widgetConfigurator = widgetConfigurator,
+                            interactionHandler = interactionHandler,
                         )
-                    },
-                    gridState = gridState,
-                    contentListState = contentListState,
-                    selectedKey = selectedKey,
-                    widgetConfigurator = widgetConfigurator,
-                )
+                    }
+                }
             }
         }
 
-        if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) {
-            Toolbar(
-                setToolbarSize = { toolbarSize = it },
-                setRemoveButtonCoordinates = { removeButtonCoordinates = it },
-                onEditDone = onEditDone,
-                onOpenWidgetPicker = onOpenWidgetPicker,
-                onRemoveClicked = {
-                    val index =
-                        selectedKey.value?.let { key ->
-                            contentListState.list.indexOfFirst { it.key == key }
+        if (onOpenWidgetPicker != null && onEditDone != null) {
+            AnimatedVisibility(
+                visible = viewModel.isEditMode && isCommunalContentVisible,
+                enter =
+                    fadeIn(animationSpec = tween(durationMillis = 250, easing = LinearEasing)) +
+                        slideInVertically(
+                            animationSpec = tween(durationMillis = 1000, easing = Emphasized),
+                        ),
+                exit =
+                    fadeOut(animationSpec = tween(durationMillis = 167, easing = LinearEasing)) +
+                        slideOutVertically(
+                            animationSpec = tween(durationMillis = 1000, easing = Emphasized)
+                        ),
+            ) {
+                Toolbar(
+                    setToolbarSize = { toolbarSize = it },
+                    setRemoveButtonCoordinates = { removeButtonCoordinates = it },
+                    onEditDone = onEditDone,
+                    onOpenWidgetPicker = onOpenWidgetPicker,
+                    onRemoveClicked = {
+                        val index =
+                            selectedKey.value?.let { key ->
+                                contentListState.list.indexOfFirst { it.key == key }
+                            }
+                        index?.let {
+                            contentListState.onRemove(it)
+                            contentListState.onSaveList()
+                            viewModel.setSelectedKey(null)
                         }
-                    index?.let {
-                        contentListState.onRemove(it)
-                        contentListState.onSaveList()
-                        viewModel.setSelectedKey(null)
-                    }
-                },
-                removeEnabled = removeButtonEnabled
-            )
+                    },
+                    removeEnabled = removeButtonEnabled
+                )
+            }
         }
         if (currentPopup == PopupType.CtaTile) {
             PopupOnDismissCtaTile(viewModel::onHidePopup)
@@ -327,16 +378,6 @@
                 onCancel = viewModel::onEnableWorkProfileDialogCancel
             )
         }
-
-        // This spacer covers the edge of the LazyHorizontalGrid and prevents it from receiving
-        // touches, so that the SceneTransitionLayout can intercept the touches and allow an edge
-        // swipe back to the blank scene.
-        Spacer(
-            Modifier.height(Dimensions.GridHeight)
-                .align(Alignment.CenterStart)
-                .width(Dimensions.Spacing)
-                .pointerInput(Unit) {}
-        )
     }
 }
 
@@ -391,6 +432,7 @@
     setGridCoordinates: (coordinates: LayoutCoordinates) -> Unit,
     updateDragPositionForRemove: (offset: Offset) -> Boolean,
     widgetConfigurator: WidgetConfigurator?,
+    interactionHandler: RemoteViews.InteractionHandler?,
 ) {
     var gridModifier =
         Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
@@ -468,7 +510,8 @@
                         selected = selected && !isDragging,
                         widgetConfigurator = widgetConfigurator,
                         index = index,
-                        contentListState = contentListState
+                        contentListState = contentListState,
+                        interactionHandler = interactionHandler,
                     )
                 }
             } else {
@@ -479,7 +522,8 @@
                     size = size,
                     selected = false,
                     index = index,
-                    contentListState = contentListState
+                    contentListState = contentListState,
+                    interactionHandler = interactionHandler,
                 )
             }
         }
@@ -578,24 +622,23 @@
                 )
                 .onSizeChanged { setToolbarSize(it) },
     ) {
-        val spacerModifier = Modifier.width(ButtonDefaults.IconSpacing)
-
-        if (!removeEnabled) {
-            Button(
-                modifier = Modifier.align(Alignment.CenterStart),
-                onClick = onOpenWidgetPicker,
-                colors = filledButtonColors(),
-                contentPadding = Dimensions.ButtonPadding
-            ) {
-                Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text))
-                Spacer(spacerModifier)
-                Text(
-                    text = stringResource(R.string.hub_mode_add_widget_button_text),
-                )
-            }
+        ToolbarButton(
+            isPrimary = !removeEnabled,
+            modifier = Modifier.align(Alignment.CenterStart),
+            onClick = onOpenWidgetPicker,
+        ) {
+            Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text))
+            Text(
+                text = stringResource(R.string.hub_mode_add_widget_button_text),
+            )
         }
 
-        if (removeEnabled) {
+        AnimatedVisibility(
+            modifier = Modifier.align(Alignment.Center),
+            visible = removeEnabled,
+            enter = fadeIn(),
+            exit = fadeOut()
+        ) {
             Button(
                 onClick = onRemoveClicked,
                 colors = filledButtonColors(),
@@ -603,33 +646,97 @@
                 modifier =
                     Modifier.graphicsLayer { alpha = removeButtonAlpha }
                         .onGloballyPositioned { setRemoveButtonCoordinates(it) }
-                        .align(Alignment.Center)
             ) {
-                RemoveButtonContent(spacerModifier)
+                Row(
+                    horizontalArrangement =
+                        Arrangement.spacedBy(
+                            ButtonDefaults.IconSpacing,
+                            Alignment.CenterHorizontally
+                        ),
+                    verticalAlignment = Alignment.CenterVertically
+                ) {
+                    Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget))
+                    Text(
+                        text = stringResource(R.string.button_to_remove_widget),
+                    )
+                }
             }
         }
 
-        if (!removeEnabled) {
-            Button(
-                modifier = Modifier.align(Alignment.CenterEnd),
-                onClick = onEditDone,
-                colors = filledButtonColors(),
-                contentPadding = Dimensions.ButtonPadding
+        ToolbarButton(
+            isPrimary = !removeEnabled,
+            modifier = Modifier.align(Alignment.CenterEnd),
+            onClick = onEditDone,
+        ) {
+            Icon(
+                Icons.Default.Check,
+                stringResource(id = R.string.hub_mode_editing_exit_button_text)
+            )
+            Text(
+                text = stringResource(R.string.hub_mode_editing_exit_button_text),
+            )
+        }
+    }
+}
+
+/**
+ * Toolbar button that displays as a filled button if primary, and an outline button if secondary.
+ */
+@Composable
+private fun ToolbarButton(
+    isPrimary: Boolean = true,
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    content: @Composable RowScope.() -> Unit
+) {
+    val colors = LocalAndroidColorScheme.current
+    AnimatedVisibility(
+        visible = isPrimary,
+        modifier = modifier,
+        enter = fadeIn(),
+        exit = fadeOut()
+    ) {
+        Button(
+            onClick = onClick,
+            colors = filledButtonColors(),
+            contentPadding = Dimensions.ButtonPadding,
+        ) {
+            Row(
+                horizontalArrangement =
+                    Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally),
+                verticalAlignment = Alignment.CenterVertically
             ) {
-                Icon(
-                    Icons.Default.Check,
-                    stringResource(id = R.string.hub_mode_editing_exit_button_text)
-                )
-                Spacer(spacerModifier)
-                Text(
-                    text = stringResource(R.string.hub_mode_editing_exit_button_text),
-                )
+                content()
+            }
+        }
+    }
+
+    AnimatedVisibility(
+        visible = !isPrimary,
+        modifier = modifier,
+        enter = fadeIn(),
+        exit = fadeOut()
+    ) {
+        OutlinedButton(
+            onClick = onClick,
+            colors =
+                ButtonDefaults.outlinedButtonColors(
+                    contentColor = colors.primary,
+                ),
+            border = BorderStroke(width = 2.0.dp, color = colors.primary),
+            contentPadding = Dimensions.ButtonPadding,
+        ) {
+            Row(
+                horizontalArrangement =
+                    Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                content()
             }
         }
     }
 }
 
-@OptIn(ExperimentalAnimationApi::class)
 @Composable
 private fun AnimatedVisibilityScope.ButtonToEditWidgets(
     onClick: () -> Unit,
@@ -732,15 +839,6 @@
 }
 
 @Composable
-private fun RemoveButtonContent(spacerModifier: Modifier) {
-    Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget))
-    Spacer(spacerModifier)
-    Text(
-        text = stringResource(R.string.button_to_remove_widget),
-    )
-}
-
-@Composable
 private fun filledButtonColors(): ButtonColors {
     val colors = LocalAndroidColorScheme.current
     return ButtonDefaults.buttonColors(
@@ -759,6 +857,7 @@
     widgetConfigurator: WidgetConfigurator? = null,
     index: Int,
     contentListState: ContentListState,
+    interactionHandler: RemoteViews.InteractionHandler?,
 ) {
     when (model) {
         is CommunalContentModel.WidgetContent.Widget ->
@@ -778,7 +877,7 @@
         is CommunalContentModel.WidgetContent.PendingWidget ->
             PendingWidgetPlaceholder(model, modifier)
         is CommunalContentModel.CtaTileInViewMode -> CtaTileInViewModeContent(viewModel, modifier)
-        is CommunalContentModel.Smartspace -> SmartspaceContent(model, modifier)
+        is CommunalContentModel.Smartspace -> SmartspaceContent(interactionHandler, model, modifier)
         is CommunalContentModel.Tutorial -> TutorialContent(modifier)
         is CommunalContentModel.Umo -> Umo(viewModel, modifier)
     }
@@ -1091,13 +1190,17 @@
 
 @Composable
 private fun SmartspaceContent(
+    interactionHandler: RemoteViews.InteractionHandler?,
     model: CommunalContentModel.Smartspace,
     modifier: Modifier = Modifier,
 ) {
     AndroidView(
         modifier = modifier,
         factory = { context ->
-            AppWidgetHostView(context).apply { updateAppWidget(model.remoteViews) }
+            SmartspaceAppWidgetHostView(context).apply {
+                interactionHandler?.let { setInteractionHandler(it) }
+                updateAppWidget(model.remoteViews)
+            }
         },
         // For reusing composition in lazy lists.
         onReset = {},
@@ -1263,6 +1366,7 @@
             horizontal = ToolbarButtonPaddingHorizontal,
         )
     val IconSize = 40.dp
+    val SlideOffsetY = 30.dp
 }
 
 private object Colors {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 9e905ac..94018bb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -24,6 +24,7 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.ComposableScene
@@ -40,6 +41,7 @@
 constructor(
     private val viewModel: CommunalViewModel,
     private val dialogFactory: SystemUIDialogFactory,
+    private val interactionHandler: WidgetInteractionHandler,
 ) : ComposableScene {
     override val key = Scenes.Communal
 
@@ -53,6 +55,6 @@
 
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
-        CommunalHub(modifier, viewModel, dialogFactory)
+        CommunalHub(modifier, viewModel, interactionHandler, dialogFactory)
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt
new file mode 100644
index 0000000..3b335fa
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.ui.compose.section
+
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
+import com.android.systemui.communal.ui.compose.Communal
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+class AmbientStatusBarSection
+@Inject
+constructor(
+    private val factory: AmbientStatusBarComponent.Factory,
+) {
+    @Composable
+    fun SceneScope.AmbientStatusBar(modifier: Modifier = Modifier) {
+        AndroidView(
+            factory = { context ->
+                (LayoutInflater.from(context)
+                        .inflate(
+                            /* resource = */ R.layout.ambient_status_bar_view,
+                            /* root = */ FrameLayout(context),
+                            /* attachToRoot = */ false,
+                        ) as AmbientStatusBarView)
+                    .apply {
+                        visibility = View.VISIBLE
+                        factory.create(this).getController().apply { init() }
+                    }
+            },
+            modifier = modifier.element(Communal.Elements.StatusBar)
+        )
+    }
+}
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 27a834b..29223ce 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
@@ -28,6 +28,7 @@
 import androidx.compose.foundation.layout.asPaddingValues
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.systemBars
@@ -65,6 +66,7 @@
 import androidx.compose.ui.util.lerp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
+import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.thenIf
@@ -89,7 +91,8 @@
     object Elements {
         val NotificationScrim = ElementKey("NotificationScrim")
         val NotificationStackPlaceholder = ElementKey("NotificationStackPlaceholder")
-        val HeadsUpNotificationPlaceholder = ElementKey("HeadsUpNotificationPlaceholder")
+        val HeadsUpNotificationPlaceholder =
+            ElementKey("HeadsUpNotificationPlaceholder", scenePicker = LowestZIndexScenePicker)
         val ShelfSpace = ElementKey("ShelfSpace")
     }
 
@@ -112,10 +115,10 @@
     modifier: Modifier = Modifier,
     isPeekFromBottom: Boolean = false,
 ) {
-    Element(
-        Notifications.Elements.HeadsUpNotificationPlaceholder,
+    Box(
         modifier =
             modifier
+                .element(Notifications.Elements.HeadsUpNotificationPlaceholder)
                 .fillMaxWidth()
                 .notificationHeadsUpHeight(stackScrollView)
                 .debugBackground(viewModel, DEBUG_HUN_COLOR)
@@ -129,9 +132,7 @@
                     // Note: boundsInWindow doesn't scroll off the screen
                     stackScrollView.setHeadsUpTop(boundsInWindow.top)
                 }
-    ) {
-        content {}
-    }
+    )
 }
 
 /** Adds the space where notification stack should appear in the scene. */
@@ -155,6 +156,11 @@
             viewModel = viewModel,
             modifier = Modifier.align(Alignment.TopCenter),
         )
+        NotificationStackCutoffGuideline(
+            stackScrollView = stackScrollView,
+            viewModel = viewModel,
+            modifier = Modifier.align(Alignment.BottomCenter),
+        )
     }
 }
 
@@ -171,6 +177,7 @@
     shouldPunchHoleBehindScrim: Boolean,
     shouldFillMaxSize: Boolean = true,
     shouldReserveSpaceForNavBar: Boolean = true,
+    shouldIncludeHeadsUpSpace: Boolean = true,
     shadeMode: ShadeMode,
     modifier: Modifier = Modifier,
 ) {
@@ -187,8 +194,12 @@
         viewModel.isCurrentGestureOverscroll.collectAsStateWithLifecycle(false)
     val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
 
-    val navBarHeight =
-        with(density) { WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx() }
+    val navBarHeightPx =
+        with(density) {
+            WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx().toInt()
+        }
+    val bottomPaddingPx = if (shouldReserveSpaceForNavBar) navBarHeightPx else 0
+
     val screenHeight = LocalRawScreenHeight.current
 
     /**
@@ -352,14 +363,13 @@
                         }
                         .verticalScroll(scrollState)
                         .fillMaxWidth()
-                        .notificationStackHeight(
-                            view = stackScrollView,
-                            padding = if (shouldReserveSpaceForNavBar) navBarHeight.toInt() else 0
-                        )
+                        .notificationStackHeight(view = stackScrollView, padding = bottomPaddingPx)
                         .onSizeChanged { size -> stackHeight.intValue = size.height },
             )
         }
-        HeadsUpNotificationSpace(stackScrollView = stackScrollView, viewModel = viewModel)
+        if (shouldIncludeHeadsUpSpace) {
+            HeadsUpNotificationSpace(stackScrollView = stackScrollView, viewModel = viewModel)
+        }
     }
 }
 
@@ -395,16 +405,36 @@
     )
 }
 
+/**
+ * A 0 height horizontal spacer to be placed at the bottom-most position in the current scene, where
+ * the notification contents (stack, footer, shelf) should be drawn.
+ */
+@Composable
+fun NotificationStackCutoffGuideline(
+    stackScrollView: NotificationScrollView,
+    viewModel: NotificationsPlaceholderViewModel,
+    modifier: Modifier = Modifier,
+) {
+    Spacer(
+        modifier =
+            modifier.fillMaxWidth().height(0.dp).onGloballyPositioned { coordinates ->
+                val positionY = coordinates.positionInWindow().y
+                debugLog(viewModel) { "STACK cutoff onGloballyPositioned: y=$positionY" }
+                stackScrollView.setStackCutoff(positionY)
+            }
+    )
+}
+
 @Composable
 private fun SceneScope.NotificationPlaceholder(
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
 ) {
-    Element(
-        Notifications.Elements.NotificationStackPlaceholder,
+    Box(
         modifier =
             modifier
+                .element(Notifications.Elements.NotificationStackPlaceholder)
                 .debugBackground(viewModel, DEBUG_STACK_COLOR)
                 .onSizeChanged { size -> debugLog(viewModel) { "STACK onSizeChanged: size=$size" } }
                 .onGloballyPositioned { coordinates: LayoutCoordinates ->
@@ -417,11 +447,8 @@
                     }
                     // NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not
                     stackScrollView.setStackTop(positionInWindow.y)
-                    stackScrollView.setStackBottom(positionInWindow.y + coordinates.size.height)
                 }
-    ) {
-        content {}
-    }
+    )
 }
 
 private fun calculateCornerRadius(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt
new file mode 100644
index 0000000..8fe8703
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QSMediaMeasurePolicy.kt
@@ -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.systemui.qs.ui.composable
+
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.util.fastFirst
+import kotlin.math.max
+
+/*
+ This layout puts QS taking all horizontal space and media taking the right half of the space.
+ However, QS (in QSPanel) puts an empty view taking half the horizontal space so that it can be
+ covered by media.
+*/
+class QSMediaMeasurePolicy(
+    val qsHeight: () -> Int,
+    val mediaVerticalOffset: Density.() -> Int = { 0 },
+) : MeasurePolicy {
+    override fun MeasureScope.measure(
+        measurables: List<Measurable>,
+        constraints: Constraints
+    ): MeasureResult {
+        val qsMeasurable = measurables.fastFirst { it.layoutId == LayoutId.QS }
+        val mediaMeasurable = measurables.fastFirst { it.layoutId == LayoutId.Media }
+
+        val qsPlaceable = qsMeasurable.measure(constraints)
+        val mediaPlaceable =
+            mediaMeasurable.measure(constraints.copy(maxWidth = constraints.maxWidth / 2))
+
+        val width = qsPlaceable.width
+        val height = max(qsHeight(), mediaPlaceable.height)
+        return layout(width, height) {
+            qsPlaceable.placeRelative(0, 0)
+            mediaPlaceable.placeRelative(width / 2, mediaVerticalOffset())
+        }
+    }
+
+    enum class LayoutId {
+        QS,
+        Media,
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 8195df3..8058dcd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -29,6 +29,7 @@
 import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
@@ -62,11 +63,21 @@
 
     object SharedValues {
         val TilesSquishiness = ValueKey("QuickSettingsTileSquishiness")
+
         object SquishinessValues {
             val Default = 1f
             val LockscreenSceneStarting = 0f
             val GoneSceneStarting = 0.3f
         }
+
+        val MediaLandscapeTopOffset = ValueKey("MediaLandscapeTopOffset")
+
+        object MediaOffset {
+            val InQQS = 0.dp
+            // Brightness + padding
+            val InQS = 92.dp
+            val Default = 0.dp
+        }
     }
 }
 
@@ -77,8 +88,8 @@
     return when (val transitionState = layoutState.transitionState) {
         is TransitionState.Idle -> {
             when (transitionState.currentScene) {
-                Scenes.Shade -> QSSceneAdapter.State.QQS.takeUnless { isSplitShade }
-                        ?: QSSceneAdapter.State.QS
+                Scenes.Shade ->
+                    QSSceneAdapter.State.QQS.takeUnless { isSplitShade } ?: QSSceneAdapter.State.QS
                 Scenes.QuickSettings -> QSSceneAdapter.State.QS
                 else -> QSSceneAdapter.State.CLOSED
             }
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 0ee485c..644040d 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
@@ -42,10 +42,12 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 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
@@ -56,13 +58,17 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.CompositingStrategy
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.TransitionState
+import com.android.compose.animation.scene.animateSceneDpAsState
 import com.android.compose.animation.scene.animateSceneFloatAsState
 import com.android.compose.modifiers.thenIf
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
@@ -78,12 +84,16 @@
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.dagger.MediaModule
 import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
+import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
 import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
+import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
+import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
 import com.android.systemui.res.R
 import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
 import com.android.systemui.shade.ui.composable.Shade
@@ -96,6 +106,7 @@
 import dagger.Lazy
 import javax.inject.Inject
 import javax.inject.Named
+import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.stateIn
@@ -258,6 +269,14 @@
             }
         }
 
+        // ############# Media ###############
+        val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
+        val mediaInRow =
+            isMediaVisible &&
+                LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
+        val mediaOffset by
+            animateSceneDpAsState(value = InQS, key = MediaLandscapeTopOffset, canOverflow = false)
+
         // This is the background for the whole scene, as the elements don't necessarily provide
         // a background that extends to the edges.
         Spacer(
@@ -337,21 +356,37 @@
                     }
                     Spacer(modifier = Modifier.height(16.dp))
                     // This view has its own horizontal padding
-                    QuickSettings(
-                        viewModel.qsSceneAdapter,
-                        { viewModel.qsSceneAdapter.qsHeight },
-                        isSplitShade = false,
-                        modifier = Modifier
-                    )
+                    val content: @Composable () -> Unit = {
+                        QuickSettings(
+                            viewModel.qsSceneAdapter,
+                            { viewModel.qsSceneAdapter.qsHeight },
+                            isSplitShade = false,
+                            modifier = Modifier.layoutId(QSMediaMeasurePolicy.LayoutId.QS)
+                        )
 
-                    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
-
-                    MediaCarousel(
-                        isVisible = isMediaVisible,
-                        mediaHost = mediaHost,
-                        modifier = Modifier.fillMaxWidth(),
-                        carouselController = mediaCarouselController,
-                    )
+                        MediaCarousel(
+                            isVisible = isMediaVisible,
+                            mediaHost = mediaHost,
+                            modifier =
+                                Modifier.fillMaxWidth()
+                                    .layoutId(QSMediaMeasurePolicy.LayoutId.Media),
+                            carouselController = mediaCarouselController,
+                        )
+                    }
+                    val landscapeQsMediaMeasurePolicy = remember {
+                        QSMediaMeasurePolicy(
+                            { viewModel.qsSceneAdapter.qsHeight },
+                            { mediaOffset.roundToPx() },
+                        )
+                    }
+                    if (mediaInRow) {
+                        Layout(
+                            content = content,
+                            measurePolicy = landscapeQsMediaMeasurePolicy,
+                        )
+                    } else {
+                        content()
+                    }
                 }
             }
 
@@ -370,5 +405,16 @@
             modifier = Modifier.align(Alignment.BottomCenter),
             isPeekFromBottom = true,
         )
+        NotificationScrollingStack(
+            shadeSession = shadeSession,
+            stackScrollView = notificationStackScrollView,
+            viewModel = notificationsPlaceholderViewModel,
+            maxScrimTop = { screenHeight },
+            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
+            shouldIncludeHeadsUpSpace = false,
+            shadeMode = ShadeMode.Single,
+            modifier =
+                Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) },
+        )
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index efda4cd..4e334c2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -28,11 +28,14 @@
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.animateSceneDpAsState
 import com.android.compose.animation.scene.animateSceneFloatAsState
 import com.android.internal.policy.SystemBarUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
 import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
+import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.Default
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.viewmodel.GoneSceneViewModel
@@ -67,6 +70,7 @@
             value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting,
             key = QuickSettings.SharedValues.TilesSquishiness,
         )
+        animateSceneDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false)
         Spacer(modifier.fillMaxSize())
         HeadsUpNotificationStack(
             stackScrollView = notificationStackScrolLView.get(),
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 18e65508..22566e7 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
@@ -99,7 +99,7 @@
                         if (sceneKey == currentSceneKey) {
                             currentDestinations
                         } else {
-                            composableScene.destinationScenes.value
+                            viewModel.resolveSceneFamilies(composableScene.destinationScenes.value)
                         },
                 ) {
                     with(composableScene) {
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 10c4030..5dc833b 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
@@ -59,6 +59,13 @@
         goneToShadeTransition(durationScale = 0.9)
     }
     from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() }
+    from(
+        Scenes.Gone,
+        to = Scenes.QuickSettings,
+        key = SlightlyFasterShadeCollapse,
+    ) {
+        goneToQuickSettingsTransition(durationScale = 0.9)
+    }
     from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() }
     from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() }
     from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() }
@@ -96,11 +103,12 @@
     overscroll(Scenes.Shade, Orientation.Vertical) {
         translate(
             Notifications.Elements.NotificationScrim,
-            y = { Shade.Dimensions.ScrimOverscrollLimit }
+            y = Shade.Dimensions.ScrimOverscrollLimit
         )
+        translate(Shade.Elements.SplitShadeStartColumn, y = Shade.Dimensions.ScrimOverscrollLimit)
         translate(
-            Shade.Elements.SplitShadeStartColumn,
-            y = { Shade.Dimensions.ScrimOverscrollLimit }
+            Notifications.Elements.NotificationStackPlaceholder,
+            y = Shade.Dimensions.ScrimOverscrollLimit,
         )
     }
     overscroll(Scenes.NotificationsShade, Orientation.Vertical) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index ac3e015..b5a10ca 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -19,7 +19,10 @@
 
 import android.view.ContextThemeWrapper
 import android.view.ViewGroup
+import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsHoveredAsState
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -32,7 +35,9 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.derivedStateOf
@@ -40,6 +45,7 @@
 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.graphics.Color
 import androidx.compose.ui.graphics.TransformOrigin
 import androidx.compose.ui.graphics.graphicsLayer
@@ -58,6 +64,7 @@
 import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.ValueKey
 import com.android.compose.animation.scene.animateElementFloatAsState
+import com.android.compose.modifiers.thenIf
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.settingslib.Utils
 import com.android.systemui.battery.BatteryMeterView
@@ -69,6 +76,7 @@
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim
 import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight
 import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
@@ -79,7 +87,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel
 import com.android.systemui.statusbar.policy.Clock
-import kotlin.math.max
 
 object ShadeHeader {
     object Elements {
@@ -103,6 +110,8 @@
     object Colors {
         val ColorScheme.shadeHeaderText: Color
             get() = Color.White
+        val ColorScheme.onScrimDim: Color
+            get() = Color.DarkGray
     }
 
     object TestTags {
@@ -130,7 +139,7 @@
     val horizontalPadding =
         max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding)
 
-    val useExpandedFormat by
+    val useExpandedTextFormat by
         remember(cutoutLocation) {
             derivedStateOf {
                 cutoutLocation != CutoutLocation.CENTER ||
@@ -138,6 +147,10 @@
             }
         }
 
+    val isLargeScreenLayout =
+            LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium ||
+                    LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded
+
     val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
 
     // This layout assumes it is globally positioned at (0, 0) and is the
@@ -182,22 +195,22 @@
                                 Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
                                     .padding(horizontal = horizontalPadding)
                         ) {
+                            if (isLargeScreenLayout) {
+                                ShadeCarrierGroup(
+                                        viewModel = viewModel,
+                                        modifier = Modifier.align(Alignment.CenterVertically),
+                                )
+                            }
                             SystemIconContainer(
+                                viewModel = viewModel,
+                                isClickable = isLargeScreenLayout,
                                 modifier = Modifier.align(Alignment.CenterVertically)
                             ) {
-                                when (LocalWindowSizeClass.current.widthSizeClass) {
-                                    WindowWidthSizeClass.Medium,
-                                    WindowWidthSizeClass.Expanded ->
-                                        ShadeCarrierGroup(
-                                            viewModel = viewModel,
-                                            modifier = Modifier.align(Alignment.CenterVertically),
-                                        )
-                                }
                                 StatusIcons(
                                     viewModel = viewModel,
                                     createTintedIconManager = createTintedIconManager,
                                     statusBarIconController = statusBarIconController,
-                                    useExpandedFormat = useExpandedFormat,
+                                    useExpandedFormat = useExpandedTextFormat,
                                     modifier =
                                         Modifier.align(Alignment.CenterVertically)
                                             .padding(end = 6.dp)
@@ -206,7 +219,7 @@
                                 BatteryIcon(
                                     createBatteryMeterViewController =
                                         createBatteryMeterViewController,
-                                    useExpandedFormat = useExpandedFormat,
+                                    useExpandedFormat = useExpandedTextFormat,
                                     modifier = Modifier.align(Alignment.CenterVertically),
                                 )
                             }
@@ -322,7 +335,7 @@
                     modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically),
                 )
                 Spacer(modifier = Modifier.weight(1f))
-                SystemIconContainer {
+                SystemIconContainer(viewModel = viewModel, isClickable = false) {
                     StatusIcons(
                         viewModel = viewModel,
                         createTintedIconManager = createTintedIconManager,
@@ -531,12 +544,30 @@
 
 @Composable
 private fun SystemIconContainer(
+    viewModel: ShadeHeaderViewModel,
+    isClickable: Boolean,
     modifier: Modifier = Modifier,
     content: @Composable RowScope.() -> Unit
 ) {
-    // TODO(b/298524053): add hover state for this container
+    val interactionSource = remember { MutableInteractionSource() }
+    val isHovered by interactionSource.collectIsHoveredAsState()
+
+    val hoverModifier = Modifier
+            .clip(RoundedCornerShape(CollapsedHeight / 4))
+            .background(MaterialTheme.colorScheme.onScrimDim)
+
     Row(
-        modifier = modifier.height(CollapsedHeight),
+        modifier = modifier
+                .height(CollapsedHeight)
+                .padding(vertical = CollapsedHeight / 4)
+                .thenIf(isClickable) {
+                    Modifier.clickable(
+                            interactionSource = interactionSource,
+                            indication = null,
+                            onClick = { viewModel.onSystemIconContainerClicked() },
+                    )
+                }
+                .thenIf(isHovered) { hoverModifier },
         content = content,
     )
 }
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 312890e..edef5fb 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
@@ -25,7 +25,6 @@
 import androidx.compose.foundation.clipScrollableContainer
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
@@ -35,8 +34,8 @@
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.navigationBarsPadding
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.rememberScrollState
@@ -54,6 +53,7 @@
 import androidx.compose.ui.graphics.CompositingStrategy
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.layoutId
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
@@ -65,6 +65,7 @@
 import com.android.compose.animation.scene.TransitionState
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.animateSceneDpAsState
 import com.android.compose.animation.scene.animateSceneFloatAsState
 import com.android.compose.modifiers.padding
 import com.android.compose.modifiers.thenIf
@@ -82,9 +83,13 @@
 import com.android.systemui.media.controls.ui.view.MediaHostState
 import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
 import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
+import com.android.systemui.notifications.ui.composable.NotificationStackCutoffGuideline
 import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
 import com.android.systemui.qs.ui.composable.BrightnessMirror
+import com.android.systemui.qs.ui.composable.QSMediaMeasurePolicy
 import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
+import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQQS
 import com.android.systemui.res.R
 import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.shared.model.Scenes
@@ -112,7 +117,7 @@
     object Dimensions {
         val ScrimCornerSize = 32.dp
         val HorizontalPadding = 16.dp
-        const val ScrimOverscrollLimit = 100f
+        val ScrimOverscrollLimit = 32.dp
         const val ScrimVisibilityThreshold = 5f
     }
 
@@ -241,6 +246,8 @@
     val mediaInRow =
         isMediaVisible &&
             LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
+    val mediaOffset by
+        animateSceneDpAsState(value = InQQS, key = MediaLandscapeTopOffset, canOverflow = false)
 
     Box(
         modifier =
@@ -281,10 +288,10 @@
                                 statusBarIconController = statusBarIconController,
                             )
 
-                            val content: @Composable (Modifier) -> Unit = { modifier ->
+                            val content: @Composable () -> Unit = {
                                 Box(
                                     Modifier.element(QuickSettings.Elements.QuickQuickSettings)
-                                        .then(modifier)
+                                        .layoutId(QSMediaMeasurePolicy.LayoutId.QS)
                                 ) {
                                     QuickSettings(
                                         viewModel.qsSceneAdapter,
@@ -297,21 +304,25 @@
                                 MediaCarousel(
                                     isVisible = isMediaVisible,
                                     mediaHost = mediaHost,
-                                    modifier = Modifier.fillMaxWidth().then(modifier),
+                                    modifier =
+                                        Modifier.fillMaxWidth()
+                                            .layoutId(QSMediaMeasurePolicy.LayoutId.Media),
                                     carouselController = mediaCarouselController,
                                 )
                             }
-
-                            if (!mediaInRow) {
-                                content(Modifier)
+                            val landscapeQsMediaMeasurePolicy = remember {
+                                QSMediaMeasurePolicy(
+                                    { viewModel.qsSceneAdapter.qqsHeight },
+                                    { mediaOffset.roundToPx() },
+                                )
+                            }
+                            if (mediaInRow) {
+                                Layout(
+                                    content = content,
+                                    measurePolicy = landscapeQsMediaMeasurePolicy,
+                                )
                             } else {
-                                Row(
-                                    modifier = Modifier.fillMaxWidth(),
-                                    horizontalArrangement = spacedBy(16.dp),
-                                    verticalAlignment = Alignment.CenterVertically,
-                                ) {
-                                    content(Modifier.weight(1f))
-                                }
+                                content()
                             }
                         }
                     },
@@ -341,6 +352,11 @@
                 notificationsPlaceable.placeRelative(x = 0, y = maxNotifScrimTop.value.roundToInt())
             }
         }
+        NotificationStackCutoffGuideline(
+            stackScrollView = notificationStackScrollView,
+            viewModel = viewModel.notifications,
+            modifier = Modifier.align(Alignment.BottomCenter).navigationBarsPadding()
+        )
     }
 }
 
@@ -520,6 +536,7 @@
                     viewModel = viewModel.notifications,
                     maxScrimTop = { 0f },
                     shouldPunchHoleBehindScrim = false,
+                    shouldReserveSpaceForNavBar = false,
                     shadeMode = ShadeMode.Split,
                     modifier =
                         Modifier.weight(1f)
@@ -529,5 +546,10 @@
                 )
             }
         }
+        NotificationStackCutoffGuideline(
+            stackScrollView = notificationStackScrollView,
+            viewModel = viewModel.notifications,
+            modifier = Modifier.align(Alignment.BottomCenter).navigationBarsPadding()
+        )
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index e1ae80f..6d03118 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -88,7 +88,7 @@
                         } else {
                             MaterialTheme.colorScheme.surface
                         },
-                    shape = RoundedCornerShape(28.dp),
+                    shape = RoundedCornerShape(20.dp),
                     contentColor =
                         if (viewModel.isActive) {
                             MaterialTheme.colorScheme.onTertiaryContainer
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 1b821d3..bb2daec 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -91,7 +91,7 @@
                             contentDescription = label
                         },
                     onClick = { onCheckedChange(!viewModel.isActive) },
-                    shape = RoundedCornerShape(28.dp),
+                    shape = RoundedCornerShape(20.dp),
                     colors = colors,
                     contentPadding = PaddingValues(0.dp)
                 ) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 9891b5b..3295dde 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
 
 import android.view.Gravity
+import androidx.annotation.VisibleForTesting
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.MaterialTheme
@@ -71,7 +72,8 @@
     }
 
     @Composable
-    private fun Content(dialog: SystemUIDialog) {
+    @VisibleForTesting
+    fun Content(dialog: SystemUIDialog) {
         val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle()
 
         if (!isAvailable) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 7fd3a176..114dcf4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -381,18 +381,17 @@
                 // relayout/redraw for nothing.
                 fromValue
             } else {
-                // In the case of bouncing, if the value remains constant during the overscroll, we
-                // should use the value of the scene we are bouncing around.
-                if (!canOverflow && transition is TransitionState.HasOverscrollProperties) {
-                    val bouncingScene = transition.bouncingScene
-                    if (bouncingScene != null) {
-                        return sharedValue[bouncingScene]
-                    }
-                }
-
+                val overscrollSpec = transition.currentOverscrollSpec
                 val progress =
-                    if (canOverflow) transition.progress
-                    else transition.progress.fastCoerceIn(0f, 1f)
+                    when {
+                        overscrollSpec == null -> {
+                            if (canOverflow) transition.progress
+                            else transition.progress.fastCoerceIn(0f, 1f)
+                        }
+                        overscrollSpec.scene == transition.toScene -> 1f
+                        else -> 0f
+                    }
+
                 sharedValue.type.lerp(fromValue, toValue, progress)
             }
         } else fromValue ?: toValue
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 48a348b..c2dd803 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -109,8 +109,7 @@
                     layoutState.transitions.interruptionHandler.onInterruption(
                         transitionState,
                         target,
-                    )
-                        ?: DefaultInterruptionHandler.onInterruption(transitionState, target)
+                    ) ?: DefaultInterruptionHandler.onInterruption(transitionState, target)
 
                 val animateFrom = interruptionResult.animateFrom
                 if (
@@ -159,6 +158,7 @@
     val transition =
         if (reversed) {
             OneOffTransition(
+                key = transitionKey,
                 fromScene = targetScene,
                 toScene = fromScene,
                 currentScene = targetScene,
@@ -167,6 +167,7 @@
             )
         } else {
             OneOffTransition(
+                key = transitionKey,
                 fromScene = fromScene,
                 toScene = targetScene,
                 currentScene = targetScene,
@@ -178,7 +179,7 @@
     // Change the current layout state to start this new transition. This will compute the
     // TransformationSpec associated to this transition, which we need to initialize the Animatable
     // that will actually animate it.
-    layoutState.startTransition(transition, transitionKey, chain)
+    layoutState.startTransition(transition, chain)
 
     // The transition now contains the transformation spec that we should use to instantiate the
     // Animatable.
@@ -207,6 +208,7 @@
 }
 
 private class OneOffTransition(
+    override val key: TransitionKey?,
     fromScene: SceneKey,
     toScene: SceneKey,
     override val currentScene: SceneKey,
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 e9633c2..bb17024 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
@@ -257,7 +257,7 @@
 
     fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) {
         if (isDrivingTransition || force) {
-            layoutState.startTransition(newTransition, newTransition.key)
+            layoutState.startTransition(newTransition)
         }
 
         swipeTransition = newTransition
@@ -555,7 +555,7 @@
     val layoutImpl: SceneTransitionLayoutImpl,
     val layoutState: BaseSceneTransitionLayoutState,
     val coroutineScope: CoroutineScope,
-    val key: TransitionKey?,
+    override val key: TransitionKey?,
     val _fromScene: Scene,
     val _toScene: Scene,
     val userActionDistanceScope: UserActionDistanceScope,
@@ -692,6 +692,8 @@
         return startOffsetAnimation {
             val animatable = Animatable(dragOffset, OffsetVisibilityThreshold)
             val isTargetGreater = targetOffset > animatable.value
+            val startedWhenOvercrollingTargetScene =
+                if (targetScene == fromScene) progress < 0f else progress > 1f
             val job =
                 coroutineScope
                     // Important: We start atomically to make sure that we start the coroutine even
@@ -703,6 +705,8 @@
                         // coroutine if we don't need to animate.
                         if (skipAnimation) {
                             snapToScene(targetScene)
+                            cancelOffsetAnimation()
+                            dragOffset = targetOffset
                             return@launch
                         }
 
@@ -718,10 +722,19 @@
                                 if (bouncingScene == null) {
                                     val isBouncing =
                                         if (isTargetGreater) {
-                                            value > targetOffset
+                                            if (startedWhenOvercrollingTargetScene) {
+                                                value >= targetOffset
+                                            } else {
+                                                value > targetOffset
+                                            }
                                         } else {
-                                            value < targetOffset
+                                            if (startedWhenOvercrollingTargetScene) {
+                                                value <= targetOffset
+                                            } else {
+                                                value < targetOffset
+                                            }
                                         }
+
                                     if (isBouncing) {
                                         bouncingScene = targetScene
 
@@ -739,7 +752,6 @@
                                 }
                             }
                         } finally {
-                            bouncingScene = null
                             snapToScene(targetScene)
                         }
                     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 11d3841..09d11b7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -39,6 +39,8 @@
 import androidx.compose.ui.layout.Placeable
 import androidx.compose.ui.node.DrawModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.TraversableNode
+import androidx.compose.ui.node.traverseDescendants
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.IntSize
@@ -66,6 +68,9 @@
      */
     var lastTransition: TransitionState.Transition? = null
 
+    /** Whether this element was ever drawn in a scene. */
+    var wasDrawnInAnyScene = false
+
     override fun toString(): String {
         return "Element(key=$key)"
     }
@@ -162,7 +167,7 @@
     private var currentTransitions: List<TransitionState.Transition>,
     private var scene: Scene,
     private var key: ElementKey,
-) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode {
+) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode, TraversableNode {
     private var _element: Element? = null
     private val element: Element
         get() = _element!!
@@ -171,6 +176,8 @@
     private val sceneState: Element.SceneState
         get() = _sceneState!!
 
+    override val traverseKey: Any = ElementTraverseKey
+
     override fun onAttach() {
         super.onAttach()
         updateElementAndSceneValues()
@@ -277,7 +284,7 @@
         constraints: Constraints,
     ): MeasureResult {
         val transitions = currentTransitions
-        val transition = elementTransition(element, transitions)
+        val transition = elementTransition(layoutImpl, element, transitions)
 
         // If this element is not supposed to be laid out now, either because it is not part of any
         // ongoing transition or the other scene of its transition is overscrolling, then lay out
@@ -286,34 +293,136 @@
         val isOtherSceneOverscrolling = overscrollScene != null && overscrollScene != scene.key
         val isNotPartOfAnyOngoingTransitions = transitions.isNotEmpty() && transition == null
         if (isNotPartOfAnyOngoingTransitions || isOtherSceneOverscrolling) {
-            sceneState.lastOffset = Offset.Unspecified
-            sceneState.lastScale = Scale.Unspecified
-            sceneState.lastAlpha = Element.AlphaUnspecified
+            recursivelyClearPlacementValues()
+            sceneState.lastSize = Element.SizeUnspecified
 
             val placeable = measurable.measure(constraints)
-            sceneState.lastSize = placeable.size()
-
             return layout(placeable.width, placeable.height) { /* Do not place */ }
         }
 
         val placeable =
-            measure(layoutImpl, scene, element, transition, sceneState, measurable, constraints)
+            measure(layoutImpl, element, transition, sceneState, measurable, constraints)
         sceneState.lastSize = placeable.size()
-        return layout(placeable.width, placeable.height) {
-            place(
-                layoutImpl,
-                scene,
-                element,
-                transition,
-                sceneState,
-                placeable,
-            )
+        return layout(placeable.width, placeable.height) { place(transition, placeable) }
+    }
+
+    @OptIn(ExperimentalComposeUiApi::class)
+    private fun Placeable.PlacementScope.place(
+        transition: TransitionState.Transition?,
+        placeable: Placeable,
+    ) {
+        with(layoutImpl.lookaheadScope) {
+            // Update the offset (relative to the SceneTransitionLayout) this element has in this
+            // scene when idle.
+            val coords =
+                coordinates ?: error("Element ${element.key} does not have any coordinates")
+
+            // No need to place the element in this scene if we don't want to draw it anyways.
+            if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) {
+                recursivelyClearPlacementValues()
+                return
+            }
+
+            val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
+            val targetOffset =
+                computeValue(
+                    layoutImpl,
+                    sceneState,
+                    element,
+                    transition,
+                    sceneValue = { it.targetOffset },
+                    transformation = { it.offset },
+                    currentValue = { currentOffset },
+                    isSpecified = { it != Offset.Unspecified },
+                    ::lerp,
+                )
+
+            val interruptedOffset =
+                computeInterruptedValue(
+                    layoutImpl,
+                    transition,
+                    value = targetOffset,
+                    unspecifiedValue = Offset.Unspecified,
+                    zeroValue = Offset.Zero,
+                    getValueBeforeInterruption = { sceneState.offsetBeforeInterruption },
+                    setValueBeforeInterruption = { sceneState.offsetBeforeInterruption = it },
+                    getInterruptionDelta = { sceneState.offsetInterruptionDelta },
+                    setInterruptionDelta = { delta ->
+                        setPlacementInterruptionDelta(
+                            element = element,
+                            sceneState = sceneState,
+                            transition = transition,
+                            delta = delta,
+                            setter = { sceneState, delta ->
+                                sceneState.offsetInterruptionDelta = delta
+                            },
+                        )
+                    },
+                    diff = { a, b -> a - b },
+                    add = { a, b, bProgress -> a + b * bProgress },
+                )
+
+            sceneState.lastOffset = interruptedOffset
+
+            val offset = (interruptedOffset - currentOffset).round()
+            if (
+                isElementOpaque(scene, element, transition) &&
+                    interruptedAlpha(layoutImpl, element, transition, sceneState, alpha = 1f) == 1f
+            ) {
+                sceneState.lastAlpha = 1f
+
+                // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is
+                // not animated once b/305195729 is fixed. Test that drawing is not invalidated in
+                // that case.
+                placeable.place(offset)
+            } else {
+                placeable.placeWithLayer(offset) {
+                    // This layer might still run on its own (outside of the placement phase) even
+                    // if this element is not placed or composed anymore, so we need to double check
+                    // again here before calling [elementAlpha] (which will update
+                    // [SceneState.lastAlpha]). We also need to recompute the current transition to
+                    // make sure that we are using the current transition and not a reference to an
+                    // old one. See b/343138966 for details.
+                    if (_element == null) {
+                        return@placeWithLayer
+                    }
+
+                    val transition = elementTransition(layoutImpl, element, currentTransitions)
+                    if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) {
+                        return@placeWithLayer
+                    }
+
+                    alpha = elementAlpha(layoutImpl, element, transition, sceneState)
+                    compositingStrategy = CompositingStrategy.ModulateAlpha
+                }
+            }
+        }
+    }
+
+    /**
+     * Recursively clear the last placement values on this node and all descendants ElementNodes.
+     * This should be called when this node is not placed anymore, so that we correctly clear values
+     * for the descendants for which approachMeasure() won't be called.
+     */
+    private fun recursivelyClearPlacementValues() {
+        fun Element.SceneState.clearLastPlacementValues() {
+            lastOffset = Offset.Unspecified
+            lastScale = Scale.Unspecified
+            lastAlpha = Element.AlphaUnspecified
+        }
+
+        sceneState.clearLastPlacementValues()
+        traverseDescendants(ElementTraverseKey) { node ->
+            (node as ElementNode)._sceneState?.clearLastPlacementValues()
+            TraversableNode.Companion.TraverseDescendantsAction.ContinueTraversal
         }
     }
 
     override fun ContentDrawScope.draw() {
-        val transition = elementTransition(element, currentTransitions)
-        val drawScale = getDrawScale(layoutImpl, scene, element, transition, sceneState)
+        element.wasDrawnInAnyScene = true
+
+        val transition = elementTransition(layoutImpl, element, currentTransitions)
+        val drawScale = getDrawScale(layoutImpl, element, transition, sceneState)
         if (drawScale == Scale.Default) {
             drawContent()
         } else {
@@ -328,6 +437,8 @@
     }
 
     companion object {
+        private val ElementTraverseKey = Any()
+
         private fun maybePruneMaps(
             layoutImpl: SceneTransitionLayoutImpl,
             element: Element,
@@ -353,6 +464,7 @@
  * its scenes contains the element.
  */
 private fun elementTransition(
+    layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
     transitions: List<TransitionState.Transition>,
 ): TransitionState.Transition? {
@@ -366,7 +478,7 @@
 
     if (transition != previousTransition && transition != null && previousTransition != null) {
         // The previous transition was interrupted by another transition.
-        prepareInterruption(element, transition, previousTransition)
+        prepareInterruption(layoutImpl, element, transition, previousTransition)
     } else if (transition == null && previousTransition != null) {
         // The transition was just finished.
         element.sceneStates.values.forEach {
@@ -379,18 +491,44 @@
 }
 
 private fun prepareInterruption(
+    layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
     transition: TransitionState.Transition,
     previousTransition: TransitionState.Transition,
 ) {
     val sceneStates = element.sceneStates
-    sceneStates[previousTransition.fromScene]?.selfUpdateValuesBeforeInterruption()
-    sceneStates[previousTransition.toScene]?.selfUpdateValuesBeforeInterruption()
-    sceneStates[transition.fromScene]?.selfUpdateValuesBeforeInterruption()
-    sceneStates[transition.toScene]?.selfUpdateValuesBeforeInterruption()
+    fun updatedSceneState(key: SceneKey): Element.SceneState? {
+        return sceneStates[key]?.also { it.selfUpdateValuesBeforeInterruption() }
+    }
+
+    val previousFromState = updatedSceneState(previousTransition.fromScene)
+    val previousToState = updatedSceneState(previousTransition.toScene)
+    val fromState = updatedSceneState(transition.fromScene)
+    val toState = updatedSceneState(transition.toScene)
 
     reconcileStates(element, previousTransition)
     reconcileStates(element, transition)
+
+    // Remove the interruption values to all scenes but the scene(s) where the element will be
+    // placed, to make sure that interruption deltas are computed only right after this interruption
+    // is prepared.
+    fun cleanInterruptionValues(sceneState: Element.SceneState) {
+        sceneState.sizeInterruptionDelta = IntSize.Zero
+        sceneState.offsetInterruptionDelta = Offset.Zero
+        sceneState.alphaInterruptionDelta = 0f
+        sceneState.scaleInterruptionDelta = Scale.Zero
+
+        if (!shouldPlaceElement(layoutImpl, sceneState.scene, element, transition)) {
+            sceneState.offsetBeforeInterruption = Offset.Unspecified
+            sceneState.alphaBeforeInterruption = Element.AlphaUnspecified
+            sceneState.scaleBeforeInterruption = Scale.Unspecified
+        }
+    }
+
+    previousFromState?.let { cleanInterruptionValues(it) }
+    previousToState?.let { cleanInterruptionValues(it) }
+    fromState?.let { cleanInterruptionValues(it) }
+    toState?.let { cleanInterruptionValues(it) }
 }
 
 /**
@@ -497,9 +635,38 @@
     }
 }
 
+/**
+ * Set the interruption delta of a *placement/drawing*-related value (offset, alpha, scale). This
+ * ensures that the delta is also set on the other scene in the transition for shared elements, so
+ * that there is no jump cut if the scene where the element is placed has changed.
+ */
+private inline fun <T> setPlacementInterruptionDelta(
+    element: Element,
+    sceneState: Element.SceneState,
+    transition: TransitionState.Transition?,
+    delta: T,
+    setter: (Element.SceneState, T) -> Unit,
+) {
+    // Set the interruption delta on the current scene.
+    setter(sceneState, delta)
+
+    if (transition == null) {
+        return
+    }
+
+    // If the element is shared, also set the delta on the other scene so that it is used by that
+    // scene if we start overscrolling it and change the scene where the element is placed.
+    val otherScene =
+        if (sceneState.scene == transition.fromScene) transition.toScene else transition.fromScene
+    val otherSceneState = element.sceneStates[otherScene] ?: return
+    if (isSharedElementEnabled(element.key, transition)) {
+        setter(otherSceneState, delta)
+    }
+}
+
 private fun shouldPlaceElement(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
+    scene: SceneKey,
     element: Element,
     transition: TransitionState.Transition?,
 ): Boolean {
@@ -510,16 +677,13 @@
 
     // Don't place the element in this scene if this scene is not part of the current element
     // transition.
-    if (scene.key != transition.fromScene && scene.key != transition.toScene) {
+    if (scene != transition.fromScene && scene != transition.toScene) {
         return false
     }
 
-    // Place the element if it is not shared or if the current scene is the one that is currently
-    // overscrolling with [OverscrollSpec].
+    // Place the element if it is not shared.
     if (
-        transition.fromScene !in element.sceneStates ||
-            transition.toScene !in element.sceneStates ||
-            transition.currentOverscrollSpec?.scene == scene.key
+        transition.fromScene !in element.sceneStates || transition.toScene !in element.sceneStates
     ) {
         return true
     }
@@ -529,20 +693,26 @@
         return true
     }
 
-    return shouldDrawOrComposeSharedElement(
+    return shouldPlaceOrComposeSharedElement(
         layoutImpl,
-        scene.key,
+        scene,
         element.key,
         transition,
     )
 }
 
-internal fun shouldDrawOrComposeSharedElement(
+internal fun shouldPlaceOrComposeSharedElement(
     layoutImpl: SceneTransitionLayoutImpl,
     scene: SceneKey,
     element: ElementKey,
     transition: TransitionState.Transition,
 ): Boolean {
+    // If we are overscrolling, only place/compose the element in the overscrolling scene.
+    val overscrollScene = transition.currentOverscrollSpec?.scene
+    if (overscrollScene != null) {
+        return scene == overscrollScene
+    }
+
     val scenePicker = element.scenePicker
     val fromScene = transition.fromScene
     val toScene = transition.toScene
@@ -555,7 +725,7 @@
             toSceneZIndex = layoutImpl.scenes.getValue(toScene).zIndex,
         ) ?: return false
 
-    return pickedScene == scene || transition.currentOverscrollSpec?.scene == scene
+    return pickedScene == scene
 }
 
 private fun isSharedElementEnabled(
@@ -629,7 +799,6 @@
  */
 private fun elementAlpha(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
     element: Element,
     transition: TransitionState.Transition?,
     sceneState: Element.SceneState,
@@ -637,25 +806,31 @@
     val alpha =
         computeValue(
                 layoutImpl,
-                scene,
+                sceneState,
                 element,
                 transition,
                 sceneValue = { 1f },
                 transformation = { it.alpha },
-                idleValue = 1f,
                 currentValue = { 1f },
                 isSpecified = { true },
                 ::lerp,
             )
             .fastCoerceIn(0f, 1f)
 
-    val interruptedAlpha = interruptedAlpha(layoutImpl, transition, sceneState, alpha)
+    // If the element is fading during this transition and that it is drawn for the first time, make
+    // sure that it doesn't instantly appear on screen.
+    if (!element.wasDrawnInAnyScene && alpha > 0f) {
+        element.sceneStates.forEach { it.value.alphaBeforeInterruption = 0f }
+    }
+
+    val interruptedAlpha = interruptedAlpha(layoutImpl, element, transition, sceneState, alpha)
     sceneState.lastAlpha = interruptedAlpha
     return interruptedAlpha
 }
 
 private fun interruptedAlpha(
     layoutImpl: SceneTransitionLayoutImpl,
+    element: Element,
     transition: TransitionState.Transition?,
     sceneState: Element.SceneState,
     alpha: Float,
@@ -669,15 +844,22 @@
         getValueBeforeInterruption = { sceneState.alphaBeforeInterruption },
         setValueBeforeInterruption = { sceneState.alphaBeforeInterruption = it },
         getInterruptionDelta = { sceneState.alphaInterruptionDelta },
-        setInterruptionDelta = { sceneState.alphaInterruptionDelta = it },
+        setInterruptionDelta = { delta ->
+            setPlacementInterruptionDelta(
+                element = element,
+                sceneState = sceneState,
+                transition = transition,
+                delta = delta,
+                setter = { sceneState, delta -> sceneState.alphaInterruptionDelta = delta },
+            )
+        },
         diff = { a, b -> a - b },
         add = { a, b, bProgress -> a + b * bProgress },
     )
 }
 
-private fun ApproachMeasureScope.measure(
+private fun measure(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
     element: Element,
     transition: TransitionState.Transition?,
     sceneState: Element.SceneState,
@@ -692,12 +874,11 @@
     val targetSize =
         computeValue(
             layoutImpl,
-            scene,
+            sceneState,
             element,
             transition,
             sceneValue = { it.targetSize },
             transformation = { it.size },
-            idleValue = lookaheadSize,
             currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() },
             isSpecified = { it != Element.SizeUnspecified },
             ::lerp,
@@ -743,7 +924,6 @@
 
 private fun ContentDrawScope.getDrawScale(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
     element: Element,
     transition: TransitionState.Transition?,
     sceneState: Element.SceneState,
@@ -751,12 +931,11 @@
     val scale =
         computeValue(
             layoutImpl,
-            scene,
+            sceneState,
             element,
             transition,
             sceneValue = { Scale.Default },
             transformation = { it.drawScale },
-            idleValue = Scale.Default,
             currentValue = { Scale.Default },
             isSpecified = { true },
             ::lerp,
@@ -776,7 +955,15 @@
             getValueBeforeInterruption = { sceneState.scaleBeforeInterruption },
             setValueBeforeInterruption = { sceneState.scaleBeforeInterruption = it },
             getInterruptionDelta = { sceneState.scaleInterruptionDelta },
-            setInterruptionDelta = { sceneState.scaleInterruptionDelta = it },
+            setInterruptionDelta = { delta ->
+                setPlacementInterruptionDelta(
+                    element = element,
+                    sceneState = sceneState,
+                    transition = transition,
+                    delta = delta,
+                    setter = { sceneState, delta -> sceneState.scaleInterruptionDelta = delta },
+                )
+            },
             diff = { a, b ->
                 Scale(
                     scaleX = a.scaleX - b.scaleX,
@@ -807,84 +994,6 @@
     return interruptedScale
 }
 
-@OptIn(ExperimentalComposeUiApi::class)
-private fun Placeable.PlacementScope.place(
-    layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
-    element: Element,
-    transition: TransitionState.Transition?,
-    sceneState: Element.SceneState,
-    placeable: Placeable,
-) {
-    with(layoutImpl.lookaheadScope) {
-        // Update the offset (relative to the SceneTransitionLayout) this element has in this scene
-        // when idle.
-        val coords = coordinates ?: error("Element ${element.key} does not have any coordinates")
-        val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords)
-
-        // No need to place the element in this scene if we don't want to draw it anyways.
-        if (!shouldPlaceElement(layoutImpl, scene, element, transition)) {
-            sceneState.lastOffset = Offset.Unspecified
-            sceneState.lastScale = Scale.Unspecified
-            sceneState.lastAlpha = Element.AlphaUnspecified
-
-            sceneState.clearValuesBeforeInterruption()
-            sceneState.clearInterruptionDeltas()
-            return
-        }
-
-        val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
-        val targetOffset =
-            computeValue(
-                layoutImpl,
-                scene,
-                element,
-                transition,
-                sceneValue = { it.targetOffset },
-                transformation = { it.offset },
-                idleValue = targetOffsetInScene,
-                currentValue = { currentOffset },
-                isSpecified = { it != Offset.Unspecified },
-                ::lerp,
-            )
-
-        val interruptedOffset =
-            computeInterruptedValue(
-                layoutImpl,
-                transition,
-                value = targetOffset,
-                unspecifiedValue = Offset.Unspecified,
-                zeroValue = Offset.Zero,
-                getValueBeforeInterruption = { sceneState.offsetBeforeInterruption },
-                setValueBeforeInterruption = { sceneState.offsetBeforeInterruption = it },
-                getInterruptionDelta = { sceneState.offsetInterruptionDelta },
-                setInterruptionDelta = { sceneState.offsetInterruptionDelta = it },
-                diff = { a, b -> a - b },
-                add = { a, b, bProgress -> a + b * bProgress },
-            )
-
-        sceneState.lastOffset = interruptedOffset
-
-        val offset = (interruptedOffset - currentOffset).round()
-        if (
-            isElementOpaque(scene, element, transition) &&
-                interruptedAlpha(layoutImpl, transition, sceneState, alpha = 1f) == 1f
-        ) {
-            sceneState.lastAlpha = 1f
-
-            // TODO(b/291071158): Call placeWithLayer() if offset != IntOffset.Zero and size is not
-            // animated once b/305195729 is fixed. Test that drawing is not invalidated in that
-            // case.
-            placeable.place(offset)
-        } else {
-            placeable.placeWithLayer(offset) {
-                alpha = elementAlpha(layoutImpl, scene, element, transition, sceneState)
-                compositingStrategy = CompositingStrategy.ModulateAlpha
-            }
-        }
-    }
-}
-
 /**
  * Return the value that should be used depending on the current layout state and transition.
  *
@@ -893,11 +1002,12 @@
  * Measurable.
  *
  * @param layoutImpl the [SceneTransitionLayoutImpl] associated to [element].
- * @param scene the scene containing [element].
+ * @param currentSceneState the scene state of the scene for which we are computing the value. Note
+ *   that during interruptions, this could be the state of a scene that is neither
+ *   [transition.toScene] nor [transition.fromScene].
  * @param element the element being animated.
  * @param sceneValue the value being animated.
  * @param transformation the transformation associated to the value being animated.
- * @param idleValue the value when idle, i.e. when there is no transition happening.
  * @param currentValue the value that would be used if it is not transformed. Note that this is
  *   different than [idleValue] even if the value is not transformed directly because it could be
  *   impacted by the transformations on other elements, like a parent that is being translated or
@@ -907,12 +1017,11 @@
  */
 private inline fun <T> computeValue(
     layoutImpl: SceneTransitionLayoutImpl,
-    scene: Scene,
+    currentSceneState: Element.SceneState,
     element: Element,
     transition: TransitionState.Transition?,
     sceneValue: (Element.SceneState) -> T,
     transformation: (ElementTransformations) -> PropertyTransformation<T>?,
-    idleValue: T,
     currentValue: () -> T,
     isSpecified: (T) -> Boolean,
     lerp: (T, T, Float) -> T,
@@ -934,19 +1043,22 @@
     if (fromState == null && toState == null) {
         // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not
         // run anymore.
-        return idleValue
+        return sceneValue(currentSceneState)
     }
 
+    val currentScene = currentSceneState.scene
     if (transition is TransitionState.HasOverscrollProperties) {
         val overscroll = transition.currentOverscrollSpec
-        if (overscroll?.scene == scene.key) {
-            val elementSpec = overscroll.transformationSpec.transformations(element.key, scene.key)
+        if (overscroll?.scene == currentScene) {
+            val elementSpec =
+                overscroll.transformationSpec.transformations(element.key, currentScene)
             val propertySpec = transformation(elementSpec) ?: return currentValue()
-            val overscrollState = checkNotNull(if (scene.key == toScene) toState else fromState)
+            val overscrollState = checkNotNull(if (currentScene == toScene) toState else fromState)
+            val idleValue = sceneValue(overscrollState)
             val targetValue =
                 propertySpec.transform(
                     layoutImpl,
-                    scene,
+                    currentScene,
                     element,
                     overscrollState,
                     transition,
@@ -990,24 +1102,30 @@
         return if (start == end) start else lerp(start, end, transition.progress)
     }
 
-    val transformation =
-        transformation(transition.transformationSpec.transformations(element.key, scene.key))
-            // If there is no transformation explicitly associated to this element value, let's use
-            // the value given by the system (like the current position and size given by the layout
-            // pass).
-            ?: return currentValue()
-
     // Get the transformed value, i.e. the target value at the beginning (for entering elements) or
     // end (for leaving elements) of the transition.
     val sceneState =
         checkNotNull(
             when {
-                isSharedElement && scene.key == fromScene -> fromState
+                isSharedElement && currentScene == fromScene -> fromState
                 isSharedElement -> toState
                 else -> fromState ?: toState
             }
         )
 
+    // The scene for which we compute the transformation. Note that this is not necessarily
+    // [currentScene] because [currentScene] could be a different scene than the transition
+    // fromScene or toScene during interruptions.
+    val scene = sceneState.scene
+
+    val transformation =
+        transformation(transition.transformationSpec.transformations(element.key, scene))
+            // If there is no transformation explicitly associated to this element value, let's use
+            // the value given by the system (like the current position and size given by the layout
+            // pass).
+            ?: return currentValue()
+
+    val idleValue = sceneValue(sceneState)
     val targetValue =
         transformation.transform(
             layoutImpl,
@@ -1029,7 +1147,7 @@
     val rangeProgress = transformation.range?.progress(progress) ?: progress
 
     // Interpolate between the value at rest and the value before entering/after leaving.
-    val isEntering = scene.key == toScene
+    val isEntering = scene == toScene
     return if (isEntering) {
         lerp(targetValue, idleValue, rangeProgress)
     } else {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index be005ea..32eadde 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -187,7 +187,7 @@
         } ?: return false
 
     // Always compose movable elements in the scene picked by their scene picker.
-    return shouldDrawOrComposeSharedElement(
+    return shouldPlaceOrComposeSharedElement(
         layoutImpl,
         scene,
         element,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 3cc8431..6001f1f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -19,8 +19,6 @@
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.awaitHorizontalTouchSlopOrCancellation
 import androidx.compose.foundation.gestures.awaitVerticalTouchSlopOrCancellation
-import androidx.compose.foundation.gestures.horizontalDrag
-import androidx.compose.foundation.gestures.verticalDrag
 import androidx.compose.runtime.Stable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
@@ -32,7 +30,9 @@
 import androidx.compose.ui.input.pointer.PointerInputScope
 import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
 import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
+import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
 import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.input.pointer.positionChangeIgnoreConsumed
 import androidx.compose.ui.input.pointer.util.VelocityTracker
 import androidx.compose.ui.input.pointer.util.addPointerInputChange
 import androidx.compose.ui.node.CompositionLocalConsumerModifierNode
@@ -46,6 +46,8 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.Velocity
 import androidx.compose.ui.util.fastAll
+import androidx.compose.ui.util.fastAny
+import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastForEach
 import kotlin.coroutines.cancellation.CancellationException
 import kotlin.math.sign
@@ -236,8 +238,23 @@
         onDragCancel: (controller: DragController) -> Unit,
         swipeDetector: SwipeDetector,
     ) {
-        // Wait for a consumable event in [PointerEventPass.Main] pass
-        val consumablePointer = awaitConsumableEvent().changes.first()
+        val consumablePointer =
+            awaitConsumableEvent {
+                    // We are searching for an event that can be used as the starting point for the
+                    // drag gesture. Our options are:
+                    // - Initial: These events should never be consumed by the MultiPointerDraggable
+                    //   since our ancestors can consume the gesture, but we would eliminate this
+                    //   possibility for our descendants.
+                    // - Main: These events are consumed during the drag gesture, and they are a
+                    //   good place to start if the previous event has not been consumed.
+                    // - Final: If the previous event has been consumed, we can wait for the Main
+                    //   pass to finish. If none of our ancestors were interested in the event, we
+                    //   can wait for an unconsumed event in the Final pass.
+                    val previousConsumed = currentEvent.changes.fastAny { it.isConsumed }
+                    if (previousConsumed) PointerEventPass.Final else PointerEventPass.Main
+                }
+                .changes
+                .first()
 
         var overSlop = 0f
         val drag =
@@ -297,18 +314,22 @@
                 onDrag(controller, drag, overSlop)
 
                 successful =
-                    when (orientation) {
-                        Orientation.Horizontal ->
-                            horizontalDrag(drag.id) {
-                                onDrag(controller, it, it.positionChange().toFloat())
-                                it.consume()
-                            }
-                        Orientation.Vertical ->
-                            verticalDrag(drag.id) {
-                                onDrag(controller, it, it.positionChange().toFloat())
-                                it.consume()
-                            }
-                    }
+                    drag(
+                        initialPointerId = drag.id,
+                        hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
+                        onDrag = {
+                            onDrag(controller, it, it.positionChange().toFloat())
+                            it.consume()
+                        },
+                        onIgnoredEvent = {
+                            // We are still dragging an object, but this event is not of interest to
+                            // the caller.
+                            // This event will not trigger the onDrag event, but we will consume the
+                            // event to prevent another pointerInput from interrupting the current
+                            // gesture just because the event was ignored.
+                            it.consume()
+                        },
+                    )
             } catch (t: Throwable) {
                 onDragCancel(controller)
                 throw t
@@ -322,7 +343,9 @@
         }
     }
 
-    private suspend fun AwaitPointerEventScope.awaitConsumableEvent(): PointerEvent {
+    private suspend fun AwaitPointerEventScope.awaitConsumableEvent(
+        pass: () -> PointerEventPass,
+    ): PointerEvent {
         fun canBeConsumed(changes: List<PointerInputChange>): Boolean {
             // All pointers must be:
             return changes.fastAll {
@@ -337,9 +360,7 @@
 
         var event: PointerEvent
         do {
-            // To allow the descendants with the opportunity to consume the event, we wait for it in
-            // the Main pass.
-            event = awaitPointerEvent()
+            event = awaitPointerEvent(pass = pass())
         } while (!canBeConsumed(event.changes))
 
         // We found a consumable event in the Main pass
@@ -352,4 +373,82 @@
             Orientation.Horizontal -> x
         }
     }
+
+    /**
+     * Continues to read drag events until all pointers are up or the drag event is canceled. The
+     * initial pointer to use for driving the drag is [initialPointerId]. [hasDragged] passes the
+     * result whether a change was detected from the drag function or not.
+     *
+     * Whenever the pointer moves, if [hasDragged] returns true, [onDrag] is called; otherwise,
+     * [onIgnoredEvent] is called.
+     *
+     * @return true when gesture ended with all pointers up and false when the gesture was canceled.
+     *
+     * Note: Inspired by DragGestureDetector.kt
+     */
+    private suspend inline fun AwaitPointerEventScope.drag(
+        initialPointerId: PointerId,
+        hasDragged: (PointerInputChange) -> Boolean,
+        onDrag: (PointerInputChange) -> Unit,
+        onIgnoredEvent: (PointerInputChange) -> Unit,
+    ): Boolean {
+        val pointer = currentEvent.changes.fastFirstOrNull { it.id == initialPointerId }
+        val isPointerUp = pointer?.pressed != true
+        if (isPointerUp) {
+            return false // The pointer has already been lifted, so the gesture is canceled
+        }
+        var pointerId = initialPointerId
+        while (true) {
+            val change = awaitDragOrUp(pointerId, hasDragged, onIgnoredEvent) ?: return false
+
+            if (change.isConsumed) {
+                return false
+            }
+
+            if (change.changedToUpIgnoreConsumed()) {
+                return true
+            }
+
+            onDrag(change)
+            pointerId = change.id
+        }
+    }
+
+    /**
+     * Waits for a single drag in one axis, final pointer up, or all pointers are up. When
+     * [initialPointerId] has lifted, another pointer that is down is chosen to be the finger
+     * governing the drag. When the final pointer is lifted, that [PointerInputChange] is returned.
+     * When a drag is detected, that [PointerInputChange] is returned. A drag is only detected when
+     * [hasDragged] returns `true`. Events that should not be captured are passed to
+     * [onIgnoredEvent].
+     *
+     * `null` is returned if there was an error in the pointer input stream and the pointer that was
+     * down was dropped before the 'up' was received.
+     *
+     * Note: Inspired by DragGestureDetector.kt
+     */
+    private suspend inline fun AwaitPointerEventScope.awaitDragOrUp(
+        initialPointerId: PointerId,
+        hasDragged: (PointerInputChange) -> Boolean,
+        onIgnoredEvent: (PointerInputChange) -> Unit,
+    ): PointerInputChange? {
+        var pointerId = initialPointerId
+        while (true) {
+            val event = awaitPointerEvent()
+            val dragEvent = event.changes.fastFirstOrNull { it.id == pointerId } ?: return null
+            if (dragEvent.changedToUpIgnoreConsumed()) {
+                val otherDown = event.changes.fastFirstOrNull { it.pressed }
+                if (otherDown == null) {
+                    // This is the last "up"
+                    return dragEvent
+                } else {
+                    pointerId = otherDown.id
+                }
+            } else if (hasDragged(dragEvent)) {
+                return dragEvent
+            } else {
+                onIgnoredEvent(dragEvent)
+            }
+        }
+    }
 }
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 f32720c..7ea8cbd 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
@@ -293,7 +293,15 @@
                 width = fromSize.width
                 height = fromSize.height
             } else {
-                val size = lerp(fromSize, toSize, transition.progress)
+                val overscrollSpec = transition.currentOverscrollSpec
+                val progress =
+                    when {
+                        overscrollSpec == null -> transition.progress
+                        overscrollSpec.scene == transition.toScene -> 1f
+                        else -> 0f
+                    }
+
+                val size = lerp(fromSize, toSize, progress)
                 width = size.width.coerceAtLeast(0)
                 height = size.height.coerceAtLeast(0)
             }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 44affd9..a8df6f4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -226,6 +226,12 @@
         val toScene: SceneKey,
     ) : TransitionState {
         /**
+         * The key of this transition. This should usually be null, but it can be specified to use a
+         * specific set of transformations associated to this transition.
+         */
+        open val key: TransitionKey? = null
+
+        /**
          * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be
          * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or
          * when flinging quickly during a swipe gesture.
@@ -455,11 +461,7 @@
      *
      * Important: you *must* call [finishTransition] once the transition is finished.
      */
-    internal fun startTransition(
-        transition: TransitionState.Transition,
-        transitionKey: TransitionKey? = null,
-        chain: Boolean = true,
-    ) {
+    internal fun startTransition(transition: TransitionState.Transition, chain: Boolean = true) {
         checkThread()
 
         // Compute the [TransformationSpec] when the transition starts.
@@ -469,7 +471,9 @@
 
         // Update the transition specs.
         transition.transformationSpec =
-            transitions.transitionSpec(fromScene, toScene, key = transitionKey).transformationSpec()
+            transitions
+                .transitionSpec(fromScene, toScene, key = transition.key)
+                .transformationSpec()
         if (orientation != null) {
             transition.updateOverscrollSpecs(
                 fromSpec = transitions.overscrollSpec(fromScene, orientation),
@@ -568,9 +572,10 @@
                     originalTransition = transitionState,
                     fromScene = targetCurrentScene,
                     toScene = matchingLink.targetTo,
+                    key = matchingLink.targetTransitionKey,
                 )
 
-            stateLink.target.startTransition(linkedTransition, matchingLink.targetTransitionKey)
+            stateLink.target.startTransition(linkedTransition)
             activeTransitionLinks[stateLink] = linkedTransition
         }
     }
@@ -763,7 +768,7 @@
 /** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */
 internal class MutableSceneTransitionLayoutStateImpl(
     initialScene: SceneKey,
-    override var transitions: SceneTransitions,
+    override var transitions: SceneTransitions = transitions {},
     private val canChangeScene: (SceneKey) -> Boolean = { true },
     stateLinks: List<StateLink> = emptyList(),
     enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index b54afae..73ee451 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -20,7 +20,6 @@
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.TransitionState
@@ -34,7 +33,7 @@
 ) : PropertyTransformation<IntSize> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
@@ -60,7 +59,7 @@
         // This simple implementation assumes that the size of [element] is the same as the size of
         // the [anchor] in [scene], so simply transform to the size of the anchor in the other
         // scene.
-        return if (scene.key == transition.fromScene) {
+        return if (scene == transition.fromScene) {
             anchorSizeIn(transition.toScene)
         } else {
             anchorSizeIn(transition.fromScene)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 2bab4f8..70dca4c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -21,7 +21,6 @@
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.TransitionState
@@ -33,7 +32,7 @@
 ) : PropertyTransformation<Offset> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
@@ -61,7 +60,7 @@
             anchorOffsetIn(transition.toScene) ?: throwException(transition.toScene)
         val offset = anchorToOffset - anchorFromOffset
 
-        return if (scene.key == transition.toScene) {
+        return if (scene == transition.toScene) {
             Offset(
                 value.x - offset.x,
                 value.y - offset.y,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index 6704a3b..98c2dd3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -20,7 +20,7 @@
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.Scale
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.TransitionState
 
@@ -37,7 +37,7 @@
 
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index 191a8fb..aa8dc38 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -20,7 +20,7 @@
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.TransitionState
 
@@ -32,13 +32,13 @@
 ) : PropertyTransformation<Offset> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
         value: Offset
     ): Offset {
-        val sceneSize = scene.targetSize
+        val sceneSize = layoutImpl.scene(scene).targetSize
         val elementSize = sceneState.targetSize
         if (elementSize == Element.SizeUnspecified) {
             return value
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index 41f626e..ada814e 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -18,7 +18,7 @@
 
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.TransitionState
 
@@ -28,7 +28,7 @@
 ) : PropertyTransformation<Float> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index f5207dc..dca8f85 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -19,7 +19,7 @@
 import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.TransitionState
 import kotlin.math.roundToInt
@@ -35,7 +35,7 @@
 ) : PropertyTransformation<IntSize> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 603f7ba..7be9ce1 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -21,7 +21,7 @@
 import androidx.compose.ui.util.fastCoerceIn
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.TransitionState
 
@@ -61,7 +61,7 @@
     // to these internal classes.
     fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index 849c9d7..f066511 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -22,7 +22,7 @@
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.OverscrollScope
-import com.android.compose.animation.scene.Scene
+import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
 import com.android.compose.animation.scene.TransitionState
 
@@ -33,7 +33,7 @@
 ) : PropertyTransformation<Offset> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
@@ -55,7 +55,7 @@
 ) : PropertyTransformation<Offset> {
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
-        scene: Scene,
+        scene: SceneKey,
         element: Element,
         sceneState: Element.SceneState,
         transition: TransitionState.Transition,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
index 79f126d..ed98885 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
@@ -17,6 +17,7 @@
 package com.android.compose.animation.scene.transition.link
 
 import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.TransitionKey
 import com.android.compose.animation.scene.TransitionState
 import kotlinx.coroutines.Job
 
@@ -25,6 +26,7 @@
     private val originalTransition: TransitionState.Transition,
     fromScene: SceneKey,
     toScene: SceneKey,
+    override val key: TransitionKey? = null,
 ) : TransitionState.Transition(fromScene, toScene) {
 
     override val currentScene: SceneKey
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index 6e8b208..a7889e2 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -18,10 +18,13 @@
 
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.SideEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -443,4 +446,56 @@
         assertThat(lastValues[bar]?.get(SceneC)).isWithin(0.001f).of(7f)
         assertThat(lastValues[bar]?.get(SceneD)).isWithin(0.001f).of(7f)
     }
+
+    @Test
+    fun animatedValueDoesNotOverscrollWhenOverscrollIsSpecified() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions { overscroll(SceneB, Orientation.Horizontal) }
+                )
+            }
+
+        val key = ValueKey("foo")
+        val lastValues = mutableMapOf<SceneKey, Float>()
+
+        @Composable
+        fun SceneScope.animateFloat(value: Float, key: ValueKey) {
+            val animatedValue = animateSceneFloatAsState(value, key)
+            LaunchedEffect(animatedValue) {
+                snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it }
+            }
+        }
+
+        rule.setContent {
+            SceneTransitionLayout(state) {
+                scene(SceneA) { animateFloat(0f, key) }
+                scene(SceneB) { animateFloat(100f, key) }
+            }
+        }
+
+        // Overscroll on A at -100%: value should be interpolated given that there is no overscroll
+        // defined for scene A.
+        var progress by mutableStateOf(-1f)
+        rule.runOnIdle {
+            state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
+        }
+        rule.waitForIdle()
+        assertThat(lastValues[SceneA]).isWithin(0.001f).of(-100f)
+        assertThat(lastValues[SceneB]).isWithin(0.001f).of(-100f)
+
+        // Middle of the transition.
+        progress = 0.5f
+        rule.waitForIdle()
+        assertThat(lastValues[SceneA]).isWithin(0.001f).of(50f)
+        assertThat(lastValues[SceneB]).isWithin(0.001f).of(50f)
+
+        // Overscroll on B at 200%: value should not be interpolated given that there is an
+        // overscroll defined for scene B.
+        progress = 2f
+        rule.waitForIdle()
+        assertThat(lastValues[SceneA]).isWithin(0.001f).of(100f)
+        assertThat(lastValues[SceneB]).isWithin(0.001f).of(100f)
+    }
 }
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 8625482..f532e2e 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
@@ -16,6 +16,8 @@
 
 package com.android.compose.animation.scene
 
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.material3.Text
 import androidx.compose.ui.geometry.Offset
@@ -158,13 +160,14 @@
             toScene: SceneKey? = null,
             progress: Float? = null,
             isUserInputOngoing: Boolean? = null
-        ) {
+        ): Transition {
             val transition = assertThat(transitionState).isTransition()
             currentScene?.let { assertThat(transition).hasCurrentScene(it) }
             fromScene?.let { assertThat(transition).hasFromScene(it) }
             toScene?.let { assertThat(transition).hasToScene(it) }
             progress?.let { assertThat(transition).hasProgress(it) }
             isUserInputOngoing?.let { assertThat(transition).hasIsUserInputOngoing(it) }
+            return transition
         }
 
         fun onDragStarted(
@@ -1015,7 +1018,7 @@
         // Swipe up to scene B at progress = 200%.
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
         val dragController = onDragStarted(startedPosition = middle, overSlop = up(2f))
-        assertTransition(fromScene = SceneA, toScene = SceneB, progress = 2f)
+        val transition = assertTransition(fromScene = SceneA, toScene = SceneB, progress = 2f)
 
         // Release the finger.
         dragController.onDragStopped(velocity = -velocityThreshold)
@@ -1024,5 +1027,172 @@
         // 100% and that the overscroll on scene B is doing nothing, we are already idle.
         runCurrent()
         assertIdle(SceneB)
+
+        // Progress is snapped to 100%.
+        assertThat(transition).hasProgress(1f)
+    }
+
+    @Test
+    fun overscroll_releaseBetween0And100Percent_up() = runGestureTest {
+        // Make scene B overscrollable.
+        layoutState.transitions = transitions {
+            from(SceneA, to = SceneB) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
+            overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
+        }
+
+        val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+
+        val dragController = onDragStarted(startedPosition = middle, overSlop = up(0.5f))
+        val transition = assertThat(transitionState).isTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneB)
+        assertThat(transition).hasProgress(0.5f)
+
+        // Release to B.
+        dragController.onDragStopped(velocity = -velocityThreshold)
+        advanceUntilIdle()
+
+        // We didn't overscroll at the end of the transition.
+        assertIdle(SceneB)
+        assertThat(transition).hasProgress(1f)
+        assertThat(transition).hasNoOverscrollSpec()
+    }
+
+    @Test
+    fun overscroll_releaseBetween0And100Percent_down() = runGestureTest {
+        // Make scene C overscrollable.
+        layoutState.transitions = transitions {
+            from(SceneA, to = SceneC) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
+            overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
+        }
+
+        val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+
+        val dragController = onDragStarted(startedPosition = middle, overSlop = down(0.5f))
+        val transition = assertThat(transitionState).isTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneC)
+        assertThat(transition).hasProgress(0.5f)
+
+        // Release to C.
+        dragController.onDragStopped(velocity = velocityThreshold)
+        advanceUntilIdle()
+
+        // We didn't overscroll at the end of the transition.
+        assertIdle(SceneC)
+        assertThat(transition).hasProgress(1f)
+        assertThat(transition).hasNoOverscrollSpec()
+    }
+
+    @Test
+    fun overscroll_releaseAt150Percent_up() = runGestureTest {
+        // Make scene B overscrollable.
+        layoutState.transitions = transitions {
+            from(SceneA, to = SceneB) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
+            overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
+        }
+
+        val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+
+        val dragController = onDragStarted(startedPosition = middle, overSlop = up(1.5f))
+        val transition = assertThat(transitionState).isTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneB)
+        assertThat(transition).hasProgress(1.5f)
+
+        // Release to B.
+        dragController.onDragStopped(velocity = 0f)
+        advanceUntilIdle()
+
+        // We kept the overscroll at 100% so that the placement logic didn't change at the end of
+        // the animation.
+        assertIdle(SceneB)
+        assertThat(transition).hasProgress(1f)
+        assertThat(transition).hasOverscrollSpec()
+    }
+
+    @Test
+    fun overscroll_releaseAt150Percent_down() = runGestureTest {
+        // Make scene C overscrollable.
+        layoutState.transitions = transitions {
+            from(SceneA, to = SceneC) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
+            overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
+        }
+
+        val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+
+        val dragController = onDragStarted(startedPosition = middle, overSlop = down(1.5f))
+        val transition = assertThat(transitionState).isTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneC)
+        assertThat(transition).hasProgress(1.5f)
+
+        // Release to C.
+        dragController.onDragStopped(velocity = 0f)
+        advanceUntilIdle()
+
+        // We kept the overscroll at 100% so that the placement logic didn't change at the end of
+        // the animation.
+        assertIdle(SceneC)
+        assertThat(transition).hasProgress(1f)
+        assertThat(transition).hasOverscrollSpec()
+    }
+
+    @Test
+    fun overscroll_releaseAtNegativePercent_up() = runGestureTest {
+        // Make Scene A overscrollable.
+        layoutState.transitions = transitions {
+            from(SceneA, to = SceneB) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
+            overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
+        }
+
+        mutableUserActionsA.clear()
+        mutableUserActionsA[Swipe.Up] = UserActionResult(SceneB)
+
+        val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+        val dragController = onDragStarted(startedPosition = middle, overSlop = down(1f))
+        val transition = assertThat(transitionState).isTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneB)
+        assertThat(transition).hasProgress(-1f)
+
+        // Release to A.
+        dragController.onDragStopped(velocity = 0f)
+        advanceUntilIdle()
+
+        // We kept the overscroll at 100% so that the placement logic didn't change at the end of
+        // the animation.
+        assertIdle(SceneA)
+        assertThat(transition).hasProgress(0f)
+        assertThat(transition).hasOverscrollSpec()
+    }
+
+    @Test
+    fun overscroll_releaseAtNegativePercent_down() = runGestureTest {
+        // Make Scene A overscrollable.
+        layoutState.transitions = transitions {
+            from(SceneA, to = SceneC) { spec = spring(dampingRatio = Spring.DampingRatioNoBouncy) }
+            overscroll(SceneA, Orientation.Vertical) { fade(TestElements.Foo) }
+        }
+
+        mutableUserActionsA.clear()
+        mutableUserActionsA[Swipe.Down] = UserActionResult(SceneC)
+
+        val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
+        val dragController = onDragStarted(startedPosition = middle, overSlop = up(1f))
+        val transition = assertThat(transitionState).isTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneC)
+        assertThat(transition).hasProgress(-1f)
+
+        // Release to A.
+        dragController.onDragStopped(velocity = 0f)
+        advanceUntilIdle()
+
+        // We kept the overscroll at 100% so that the placement logic didn't change at the end of
+        // the animation.
+        assertIdle(SceneA)
+        assertThat(transition).hasProgress(0f)
+        assertThat(transition).hasOverscrollSpec()
     }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index beb74bc..a18da73 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -32,6 +32,7 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.pager.HorizontalPager
 import androidx.compose.foundation.pager.PagerState
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.SideEffect
@@ -46,9 +47,13 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.assertIsNotDisplayed
 import androidx.compose.ui.test.assertPositionInRootIsEqualTo
 import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo
+import androidx.compose.ui.test.hasTestTag
+import androidx.compose.ui.test.hasText
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onRoot
@@ -635,10 +640,7 @@
 
         // Change the current transition.
         rule.runOnUiThread {
-            state.startTransition(
-                transition(from = SceneA, to = SceneB, progress = { 0.5f }),
-                transitionKey = null,
-            )
+            state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.5f }))
         }
 
         // The size of Foo should still be 20dp given that the new state was not composed yet.
@@ -1167,7 +1169,7 @@
         val offsetInAToB = lerp(offsetInA, offsetInB, aToBProgress)
         val sizeInAToB = lerp(sizeInA, sizeInB, aToBProgress)
         val valueInAToB = lerp(valueInA, valueInB, aToBProgress)
-        rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) }
+        rule.runOnUiThread { state.startTransition(aToB) }
         rule
             .onNode(isElement(TestElements.Foo, SceneB))
             .assertSizeIsEqualTo(sizeInAToB)
@@ -1187,7 +1189,7 @@
                 progress = { bToCProgress },
                 interruptionProgress = { interruptionProgress },
             )
-        rule.runOnUiThread { state.startTransition(bToC, transitionKey = null) }
+        rule.runOnUiThread { state.startTransition(bToC) }
 
         // The interruption deltas, which will be multiplied by the interruption progress then added
         // to the current transition offset and size.
@@ -1329,9 +1331,9 @@
                 interruptionProgress = { bToCInterruptionProgress },
                 onFinish = neverFinish(),
             )
-        rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) }
+        rule.runOnUiThread { state.startTransition(aToB) }
         rule.waitForIdle()
-        rule.runOnUiThread { state.startTransition(bToC, transitionKey = null) }
+        rule.runOnUiThread { state.startTransition(bToC) }
 
         // Foo is placed in both B and C given that the shared transition is disabled. In B, its
         // offset is impacted by the interruption but in C it is not.
@@ -1367,7 +1369,7 @@
                 progress = { 0.7f },
                 interruptionProgress = { 1f },
             )
-        rule.runOnUiThread { state.startTransition(bToA, transitionKey = null) }
+        rule.runOnUiThread { state.startTransition(bToA) }
 
         // Foo should have the position it had in B right before the interruption.
         rule
@@ -1391,8 +1393,7 @@
                                 to = SceneB,
                                 progress = { -1f },
                                 orientation = Orientation.Horizontal
-                            ),
-                            transitionKey = null,
+                            )
                         )
                     }
             }
@@ -1418,4 +1419,522 @@
         assertThat(bState.targetSize).isNotEqualTo(Element.SizeUnspecified)
         assertThat(bState.targetOffset).isNotEqualTo(Offset.Unspecified)
     }
+
+    @Test
+    fun lastAlphaIsNotSetByOutdatedLayer() = runTest {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } }
+                )
+            }
+
+        lateinit var layoutImpl: SceneTransitionLayoutImpl
+        rule.setContent {
+            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                scene(SceneA) {}
+                scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+                scene(SceneC) { Box(Modifier.element(TestElements.Foo)) }
+            }
+        }
+
+        // Start A => B at 0.5f.
+        var aToBProgress by mutableStateOf(0.5f)
+        rule.runOnUiThread {
+            state.startTransition(
+                transition(
+                    from = SceneA,
+                    to = SceneB,
+                    progress = { aToBProgress },
+                    onFinish = neverFinish(),
+                )
+            )
+        }
+        rule.waitForIdle()
+
+        val foo = checkNotNull(layoutImpl.elements[TestElements.Foo])
+        assertThat(foo.sceneStates[SceneA]).isNull()
+
+        val fooInB = foo.sceneStates[SceneB]
+        assertThat(fooInB).isNotNull()
+        assertThat(fooInB!!.lastAlpha).isEqualTo(0.5f)
+
+        // Move the progress of A => B to 0.7f.
+        aToBProgress = 0.7f
+        rule.waitForIdle()
+        assertThat(fooInB.lastAlpha).isEqualTo(0.7f)
+
+        // Start B => C at 0.3f.
+        rule.runOnUiThread {
+            state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0.3f }))
+        }
+        rule.waitForIdle()
+        val fooInC = foo.sceneStates[SceneC]
+        assertThat(fooInC).isNotNull()
+        assertThat(fooInC!!.lastAlpha).isEqualTo(1f)
+        assertThat(fooInB.lastAlpha).isEqualTo(Element.AlphaUnspecified)
+
+        // Move the progress of A => B to 0.9f. This shouldn't change anything given that B => C is
+        // now the transition applied to Foo.
+        aToBProgress = 0.9f
+        rule.waitForIdle()
+        assertThat(fooInC.lastAlpha).isEqualTo(1f)
+        assertThat(fooInB.lastAlpha).isEqualTo(Element.AlphaUnspecified)
+    }
+
+    @Test
+    fun fadingElementsDontAppearInstantly() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions { from(SceneA, to = SceneB) { fade(TestElements.Foo) } }
+                )
+            }
+
+        lateinit var layoutImpl: SceneTransitionLayoutImpl
+        rule.setContent {
+            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                scene(SceneA) {}
+                scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+            }
+        }
+
+        // Start A => B at 60%.
+        var interruptionProgress by mutableStateOf(1f)
+        rule.runOnUiThread {
+            state.startTransition(
+                transition(
+                    from = SceneA,
+                    to = SceneB,
+                    progress = { 0.6f },
+                    interruptionProgress = { interruptionProgress },
+                )
+            )
+        }
+        rule.waitForIdle()
+
+        // Alpha of Foo should be 0f at interruption progress 100%.
+        val fooInB = layoutImpl.elements.getValue(TestElements.Foo).sceneStates.getValue(SceneB)
+        assertThat(fooInB.lastAlpha).isEqualTo(0f)
+
+        // Alpha of Foo should be 0.6f at interruption progress 0%.
+        interruptionProgress = 0f
+        rule.waitForIdle()
+        assertThat(fooInB.lastAlpha).isEqualTo(0.6f)
+
+        // Alpha of Foo should be 0.3f at interruption progress 50%.
+        interruptionProgress = 0.5f
+        rule.waitForIdle()
+        assertThat(fooInB.lastAlpha).isEqualTo(0.3f)
+    }
+
+    @Test
+    fun sharedElementIsOnlyPlacedInOverscrollingScene() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions {
+                        overscroll(SceneA, Orientation.Horizontal)
+                        overscroll(SceneB, Orientation.Horizontal)
+                    }
+                )
+            }
+
+        @Composable
+        fun SceneScope.Foo() {
+            Box(Modifier.element(TestElements.Foo).size(10.dp))
+        }
+
+        rule.setContent {
+            SceneTransitionLayout(state) {
+                scene(SceneA) { Foo() }
+                scene(SceneB) { Foo() }
+            }
+        }
+
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertDoesNotExist()
+
+        // A => B while overscrolling at scene B.
+        var progress by mutableStateOf(2f)
+        rule.runOnUiThread {
+            state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
+        }
+        rule.waitForIdle()
+
+        // Foo should only be placed in scene B.
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertExists().assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed()
+
+        // Overscroll at scene A.
+        progress = -1f
+        rule.waitForIdle()
+
+        // Foo should only be placed in scene A.
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertExists().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun sharedMovableElementIsOnlyComposedInOverscrollingScene() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions {
+                        overscroll(SceneA, Orientation.Horizontal)
+                        overscroll(SceneB, Orientation.Horizontal)
+                    }
+                )
+            }
+
+        val fooInA = "fooInA"
+        val fooInB = "fooInB"
+
+        @Composable
+        fun SceneScope.MovableFoo(text: String, modifier: Modifier = Modifier) {
+            MovableElement(TestElements.Foo, modifier) { content { Text(text) } }
+        }
+
+        rule.setContent {
+            SceneTransitionLayout(state) {
+                scene(SceneA) { MovableFoo(text = fooInA) }
+                scene(SceneB) { MovableFoo(text = fooInB) }
+            }
+        }
+
+        rule.onNode(hasText(fooInA)).assertIsDisplayed()
+        rule.onNode(hasText(fooInB)).assertDoesNotExist()
+
+        // A => B while overscrolling at scene B.
+        var progress by mutableStateOf(2f)
+        rule.runOnUiThread {
+            state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
+        }
+        rule.waitForIdle()
+
+        // Foo content should only be composed in scene B.
+        rule.onNode(hasText(fooInA)).assertDoesNotExist()
+        rule.onNode(hasText(fooInB)).assertIsDisplayed()
+
+        // Overscroll at scene A.
+        progress = -1f
+        rule.waitForIdle()
+
+        // Foo content should only be composed in scene A.
+        rule.onNode(hasText(fooInA)).assertIsDisplayed()
+        rule.onNode(hasText(fooInB)).assertDoesNotExist()
+    }
+
+    @Test
+    fun interruptionThenOverscroll() = runTest {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions {
+                        overscroll(SceneB, Orientation.Vertical) {
+                            translate(TestElements.Foo, y = 15.dp)
+                        }
+                    }
+                )
+            }
+
+        @Composable
+        fun SceneScope.SceneWithFoo(offset: DpOffset, modifier: Modifier = Modifier) {
+            Box(modifier.fillMaxSize()) {
+                Box(Modifier.offset(offset.x, offset.y).element(TestElements.Foo).size(100.dp))
+            }
+        }
+
+        rule.setContent {
+            SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                scene(SceneA) { SceneWithFoo(offset = DpOffset.Zero) }
+                scene(SceneB) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 0.dp)) }
+                scene(SceneC) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 40.dp)) }
+            }
+        }
+
+        // Start A => B at 75%.
+        rule.runOnUiThread {
+            state.startTransition(
+                transition(
+                    from = SceneA,
+                    to = SceneB,
+                    progress = { 0.75f },
+                    onFinish = neverFinish(),
+                )
+            )
+        }
+
+        // Foo should be at offset (30dp, 0dp) and placed in scene B.
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(30.dp, 0.dp)
+        rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed()
+
+        // Interrupt A => B with B => C at 0%.
+        var progress by mutableStateOf(0f)
+        var interruptionProgress by mutableStateOf(1f)
+        rule.runOnUiThread {
+            state.startTransition(
+                transition(
+                    from = SceneB,
+                    to = SceneC,
+                    progress = { progress },
+                    interruptionProgress = { interruptionProgress },
+                    orientation = Orientation.Vertical,
+                    onFinish = neverFinish(),
+                )
+            )
+        }
+
+        // Because interruption progress is at 100M, Foo should still be at offset (30dp, 0dp) but
+        // placed in scene C.
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(30.dp, 0.dp)
+
+        // Overscroll B => C on scene B at -100%. Because overscrolling on B => C translates Foo
+        // vertically by -15dp and that interruptionProgress is still 100%, we should now be at
+        // (30dp, -15dp)
+        progress = -1f
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+        rule
+            .onNode(isElement(TestElements.Foo, SceneB))
+            .assertPositionInRootIsEqualTo(30.dp, -15.dp)
+        rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed()
+
+        // Finish the interruption, we should now be at (40dp, -15dp), still on scene B.
+        interruptionProgress = 0f
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+        rule
+            .onNode(isElement(TestElements.Foo, SceneB))
+            .assertPositionInRootIsEqualTo(40.dp, -15.dp)
+        rule.onNode(isElement(TestElements.Foo, SceneC)).assertIsNotDisplayed()
+
+        // Finish the transition, we should be at the final position (40dp, 40dp) on scene C.
+        progress = 1f
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(40.dp, 40.dp)
+    }
+
+    @Test
+    fun lastPlacementValuesAreClearedOnNestedElements() {
+        val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
+
+        @Composable
+        fun SceneScope.NestedFooBar() {
+            Box(Modifier.element(TestElements.Foo)) {
+                Box(Modifier.element(TestElements.Bar).size(10.dp))
+            }
+        }
+
+        lateinit var layoutImpl: SceneTransitionLayoutImpl
+        rule.setContent {
+            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                scene(SceneA) { NestedFooBar() }
+                scene(SceneB) { NestedFooBar() }
+            }
+        }
+
+        // Idle on A: composed and placed only in B.
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed()
+        rule.onNode(isElement(TestElements.Bar, SceneA)).assertIsDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertDoesNotExist()
+        rule.onNode(isElement(TestElements.Bar, SceneB)).assertDoesNotExist()
+
+        assertThat(layoutImpl.elements).containsKey(TestElements.Foo)
+        assertThat(layoutImpl.elements).containsKey(TestElements.Bar)
+        val foo = layoutImpl.elements.getValue(TestElements.Foo)
+        val bar = layoutImpl.elements.getValue(TestElements.Bar)
+
+        assertThat(foo.sceneStates).containsKey(SceneA)
+        assertThat(bar.sceneStates).containsKey(SceneA)
+        assertThat(foo.sceneStates).doesNotContainKey(SceneB)
+        assertThat(bar.sceneStates).doesNotContainKey(SceneB)
+
+        val fooInA = foo.sceneStates.getValue(SceneA)
+        val barInA = bar.sceneStates.getValue(SceneA)
+        assertThat(fooInA.lastOffset).isNotEqualTo(Offset.Unspecified)
+        assertThat(fooInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
+        assertThat(fooInA.lastScale).isNotEqualTo(Scale.Unspecified)
+
+        assertThat(barInA.lastOffset).isNotEqualTo(Offset.Unspecified)
+        assertThat(barInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
+        assertThat(barInA.lastScale).isNotEqualTo(Scale.Unspecified)
+
+        // A => B: composed in both and placed only in B.
+        rule.runOnUiThread { state.startTransition(transition(from = SceneA, to = SceneB)) }
+        rule.onNode(isElement(TestElements.Foo, SceneA)).assertExists().assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Bar, SceneA)).assertExists().assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed()
+        rule.onNode(isElement(TestElements.Bar, SceneB)).assertIsDisplayed()
+
+        assertThat(foo.sceneStates).containsKey(SceneB)
+        assertThat(bar.sceneStates).containsKey(SceneB)
+
+        val fooInB = foo.sceneStates.getValue(SceneB)
+        val barInB = bar.sceneStates.getValue(SceneB)
+        assertThat(fooInA.lastOffset).isEqualTo(Offset.Unspecified)
+        assertThat(fooInA.lastAlpha).isEqualTo(Element.AlphaUnspecified)
+        assertThat(fooInA.lastScale).isEqualTo(Scale.Unspecified)
+        assertThat(fooInB.lastOffset).isNotEqualTo(Offset.Unspecified)
+        assertThat(fooInB.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
+        assertThat(fooInB.lastScale).isNotEqualTo(Scale.Unspecified)
+
+        assertThat(barInA.lastOffset).isEqualTo(Offset.Unspecified)
+        assertThat(barInA.lastAlpha).isEqualTo(Element.AlphaUnspecified)
+        assertThat(barInA.lastScale).isEqualTo(Scale.Unspecified)
+        assertThat(barInB.lastOffset).isNotEqualTo(Offset.Unspecified)
+        assertThat(barInB.lastAlpha).isNotEqualTo(Element.AlphaUnspecified)
+        assertThat(barInB.lastScale).isNotEqualTo(Scale.Unspecified)
+    }
+
+    @Test
+    fun currentTransitionSceneIsUsedToComputeElementValues() = runTest {
+        val state =
+            rule.runOnIdle {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions {
+                        from(SceneB, to = SceneC) {
+                            scaleSize(TestElements.Foo, width = 2f, height = 3f)
+                        }
+                    }
+                )
+            }
+
+        @Composable
+        fun SceneScope.Foo() {
+            Box(Modifier.testTag("fooParentIn${sceneKey.debugName}")) {
+                Box(Modifier.element(TestElements.Foo).size(20.dp))
+            }
+        }
+
+        rule.setContent {
+            SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                scene(SceneA) { Foo() }
+                scene(SceneB) {}
+                scene(SceneC) { Foo() }
+            }
+        }
+
+        // We have 2 transitions:
+        //  - A => B at 100%
+        //  - B => C at 0%
+        // So Foo should have a size of (40dp, 60dp) in both A and C given that it is scaling its
+        // size in B => C.
+        rule.runOnUiThread {
+            state.startTransition(
+                transition(from = SceneA, to = SceneB, progress = { 1f }, onFinish = neverFinish())
+            )
+            state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0f }))
+        }
+
+        rule.onNode(hasTestTag("fooParentInSceneA")).assertSizeIsEqualTo(40.dp, 60.dp)
+        rule.onNode(hasTestTag("fooParentInSceneC")).assertSizeIsEqualTo(40.dp, 60.dp)
+    }
+
+    @Test
+    fun interruptionDeltasAreProperlyCleaned() = runTest {
+        val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
+
+        @Composable
+        fun SceneScope.Foo(offset: Dp) {
+            Box(Modifier.fillMaxSize()) {
+                Box(Modifier.offset(offset, offset).element(TestElements.Foo).size(20.dp))
+            }
+        }
+
+        rule.setContent {
+            SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                scene(SceneA) { Foo(offset = 0.dp) }
+                scene(SceneB) { Foo(offset = 20.dp) }
+                scene(SceneC) { Foo(offset = 40.dp) }
+            }
+        }
+
+        // Start A => B at 50%.
+        val aToB =
+            transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
+        rule.runOnUiThread { state.startTransition(aToB) }
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(10.dp, 10.dp)
+
+        // Start B => C at 0%. This will compute an interruption delta of (-10dp, -10dp) so that the
+        // position of Foo is unchanged and converges to (20dp, 20dp).
+        var interruptionProgress by mutableStateOf(1f)
+        val bToC =
+            transition(
+                from = SceneB,
+                to = SceneC,
+                progress = { 0f },
+                interruptionProgress = { interruptionProgress },
+                onFinish = neverFinish(),
+            )
+        rule.runOnUiThread { state.startTransition(bToC) }
+        rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(10.dp, 10.dp)
+
+        // Finish the interruption and leave the transition progress at 0f. We should be at the same
+        // state as in B.
+        interruptionProgress = 0f
+        rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(20.dp, 20.dp)
+
+        // Finish both transitions but directly start a new one B => A with interruption progress
+        // 100%. We should be at (20dp, 20dp), unless the interruption deltas have not been
+        // correctly cleaned.
+        rule.runOnUiThread {
+            state.finishTransition(aToB, idleScene = SceneB)
+            state.finishTransition(bToC, idleScene = SceneB)
+            state.startTransition(
+                transition(
+                    from = SceneB,
+                    to = SceneA,
+                    progress = { 0f },
+                    interruptionProgress = { 1f },
+                )
+            )
+        }
+        rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(20.dp, 20.dp)
+    }
+
+    @Test
+    fun lastSizeIsUnspecifiedWhenOverscrollingOtherScene() = runTest {
+        val state =
+            rule.runOnIdle {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions { overscroll(SceneA, Orientation.Horizontal) }
+                )
+            }
+
+        @Composable
+        fun SceneScope.Foo() {
+            Box(Modifier.element(TestElements.Foo).size(10.dp))
+        }
+
+        lateinit var layoutImpl: SceneTransitionLayoutImpl
+        rule.setContent {
+            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                scene(SceneA) { Foo() }
+                scene(SceneB) { Foo() }
+            }
+        }
+
+        // Overscroll A => B on A.
+        rule.runOnUiThread {
+            state.startTransition(
+                transition(from = SceneA, to = SceneB, progress = { -1f }, onFinish = neverFinish())
+            )
+        }
+        rule.waitForIdle()
+
+        assertThat(
+                layoutImpl.elements.getValue(TestElements.Foo).sceneStates.getValue(SceneB).lastSize
+            )
+            .isEqualTo(Element.SizeUnspecified)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index 85d4165..09d1a82 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -40,7 +40,7 @@
         val state =
             MutableSceneTransitionLayoutState(
                 SceneA,
-                transitions { /* default interruption handler */},
+                transitions { /* default interruption handler */ },
             )
 
         state.setTargetScene(SceneB, coroutineScope = this)
@@ -160,7 +160,7 @@
                 progressVelocity = { progressVelocity },
                 onFinish = { launch {} },
             )
-        state.startTransition(aToB, transitionKey = null)
+        state.startTransition(aToB)
 
         // Animate back to A. The previous transition is reversed, i.e. it has the same (from, to)
         // pair, and its velocity is used when animating the progress back to 0.
@@ -186,7 +186,7 @@
                 progressVelocity = { progressVelocity },
                 onFinish = { launch {} },
             )
-        state.startTransition(aToB, transitionKey = null)
+        state.startTransition(aToB)
 
         // Animate to B. The previous transition is reversed, i.e. it has the same (from, to) pair,
         // and its velocity is used when animating the progress to 1.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index 4bb643f..1a0740b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -349,6 +349,121 @@
     }
 
     @Test
+    fun multiPointerDuringAnotherGestureWaitAConsumableEventAfterMainPass() {
+        val size = 200f
+        val middle = Offset(size / 2f, size / 2f)
+
+        var verticalStarted = false
+        var verticalDragged = false
+        var verticalStopped = false
+        var horizontalStarted = false
+        var horizontalDragged = false
+        var horizontalStopped = false
+
+        var touchSlop = 0f
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            Box(
+                Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .multiPointerDraggable(
+                        orientation = Orientation.Vertical,
+                        enabled = { true },
+                        startDragImmediately = { false },
+                        onDragStarted = { _, _, _ ->
+                            verticalStarted = true
+                            object : DragController {
+                                override fun onDrag(delta: Float) {
+                                    verticalDragged = true
+                                }
+
+                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
+                                    verticalStopped = true
+                                }
+                            }
+                        },
+                    )
+                    .multiPointerDraggable(
+                        orientation = Orientation.Horizontal,
+                        enabled = { true },
+                        startDragImmediately = { false },
+                        onDragStarted = { _, _, _ ->
+                            horizontalStarted = true
+                            object : DragController {
+                                override fun onDrag(delta: Float) {
+                                    horizontalDragged = true
+                                }
+
+                                override fun onStop(velocity: Float, canChangeScene: Boolean) {
+                                    horizontalStopped = true
+                                }
+                            }
+                        },
+                    )
+            )
+        }
+
+        fun startDraggingDown() {
+            rule.onRoot().performTouchInput {
+                down(middle)
+                moveBy(Offset(0f, touchSlop))
+            }
+        }
+
+        fun startDraggingRight() {
+            rule.onRoot().performTouchInput {
+                down(middle)
+                moveBy(Offset(touchSlop, 0f))
+            }
+        }
+
+        fun stopDragging() {
+            rule.onRoot().performTouchInput { up() }
+        }
+
+        fun continueDown() {
+            rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
+        }
+
+        fun continueRight() {
+            rule.onRoot().performTouchInput { moveBy(Offset(touchSlop, 0f)) }
+        }
+
+        startDraggingDown()
+        assertThat(verticalStarted).isTrue()
+        assertThat(verticalDragged).isTrue()
+        assertThat(verticalStopped).isFalse()
+
+        // Ignore right swipe, do not interrupt the dragging gesture.
+        continueRight()
+        assertThat(horizontalStarted).isFalse()
+        assertThat(horizontalDragged).isFalse()
+        assertThat(horizontalStopped).isFalse()
+        assertThat(verticalStopped).isFalse()
+
+        stopDragging()
+        assertThat(verticalStopped).isTrue()
+
+        verticalStarted = false
+        verticalDragged = false
+        verticalStopped = false
+
+        startDraggingRight()
+        assertThat(horizontalStarted).isTrue()
+        assertThat(horizontalDragged).isTrue()
+        assertThat(horizontalStopped).isFalse()
+
+        // Ignore down swipe, do not interrupt the dragging gesture.
+        continueDown()
+        assertThat(verticalStarted).isFalse()
+        assertThat(verticalDragged).isFalse()
+        assertThat(verticalStopped).isFalse()
+        assertThat(horizontalStopped).isFalse()
+
+        stopDragging()
+        assertThat(horizontalStopped).isTrue()
+    }
+
+    @Test
     fun multiPointerSwipeDetectorInteraction() {
         val size = 200f
         val middle = Offset(size / 2f, size / 2f)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
index 2a75e13..5543135 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
@@ -135,7 +135,7 @@
         var transitionCurrentScene by mutableStateOf(SceneA)
         val transition =
             transition(from = SceneA, to = SceneB, current = { transitionCurrentScene })
-        state.startTransition(transition, transitionKey = null)
+        state.startTransition(transition)
         assertThat(currentScene.value).isEqualTo(SceneA)
 
         // Change the transition current scene.
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
index d2c8bd6..de6f1cc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt
@@ -57,7 +57,7 @@
     @Test
     fun isTransitioningTo_transition() {
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
-        state.startTransition(transition(from = SceneA, to = SceneB), transitionKey = null)
+        state.startTransition(transition(from = SceneA, to = SceneB))
 
         assertThat(state.isTransitioning()).isTrue()
         assertThat(state.isTransitioning(from = SceneA)).isTrue()
@@ -175,7 +175,7 @@
 
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
 
@@ -211,7 +211,7 @@
 
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
         assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue()
@@ -229,7 +229,7 @@
         var progress = 0f
         val childTransition = transition(SceneA, SceneB, progress = { progress })
 
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
         assertThat(parentState.currentTransition?.progress).isEqualTo(0f)
 
         progress = .5f
@@ -242,7 +242,7 @@
 
         val childTransition = transition(SceneB, SceneA)
 
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
         assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue()
         assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
 
@@ -256,7 +256,7 @@
         val (parentState, childState) = setupLinkedStates()
 
         val childTransition = transition(SceneA, SceneB)
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
 
         childState.finishTransition(childTransition, SceneA)
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
@@ -268,7 +268,7 @@
         val (parentState, childState) = setupLinkedStates()
 
         val childTransition = transition(SceneA, SceneB)
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
 
         childState.finishTransition(childTransition, SceneD)
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
@@ -283,16 +283,16 @@
             transition(
                 SceneA,
                 SceneB,
-                onFinish = { launch { /* Do nothing. */} },
+                onFinish = { launch { /* Do nothing. */ } },
             )
         val parentTransition =
             transition(
                 SceneC,
                 SceneA,
-                onFinish = { launch { /* Do nothing. */} },
+                onFinish = { launch { /* Do nothing. */ } },
             )
-        childState.startTransition(childTransition, null)
-        parentState.startTransition(parentTransition, null)
+        childState.startTransition(childTransition)
+        parentState.startTransition(parentTransition)
 
         childState.finishTransition(childTransition, SceneB)
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
@@ -341,10 +341,7 @@
     @Test
     fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
-        state.startTransition(
-            transition(from = SceneA, to = SceneB, progress = { 0.2f }),
-            transitionKey = null
-        )
+        state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.2f }))
         assertThat(state.isTransitioning()).isTrue()
 
         // Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -360,10 +357,7 @@
     @Test
     fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
-        state.startTransition(
-            transition(from = SceneA, to = SceneB, progress = { 0.8f }),
-            transitionKey = null
-        )
+        state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.8f }))
         assertThat(state.isTransitioning()).isTrue()
 
         // Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -385,13 +379,13 @@
                 from = SceneA,
                 to = SceneB,
                 progress = { 0.5f },
-                onFinish = { launch { /* do nothing */} },
+                onFinish = { launch { /* do nothing */ } },
             )
-        state.startTransition(aToB, transitionKey = null)
+        state.startTransition(aToB)
         assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
 
         val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f })
-        state.startTransition(bToC, transitionKey = null)
+        state.startTransition(bToC)
         assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
 
         // Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -409,7 +403,7 @@
         val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD)
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
 
@@ -425,7 +419,7 @@
 
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
 
@@ -440,7 +434,7 @@
             setupLinkedStates(SceneC, SceneA, SceneB, null, SceneC, SceneD)
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition, null)
+        childState.startTransition(childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse()
     }
@@ -460,8 +454,7 @@
                 to = SceneB,
                 progress = progress,
                 orientation = Orientation.Vertical,
-            ),
-            transitionKey = null
+            )
         )
         assertThat(state.isTransitioning()).isTrue()
         return state
@@ -583,19 +576,19 @@
         assertThat(state.currentTransitions).isEmpty()
 
         // A => B.
-        state.startTransition(aToB, transitionKey = null)
+        state.startTransition(aToB)
         assertThat(finishingTransitions).isEmpty()
         assertThat(state.finishedTransitions).isEmpty()
         assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
 
         // B => C. This should automatically call finish() on aToB.
-        state.startTransition(bToC, transitionKey = null)
+        state.startTransition(bToC)
         assertThat(finishingTransitions).containsExactly(aToB)
         assertThat(state.finishedTransitions).isEmpty()
         assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
 
         // C => A. This should automatically call finish() on bToC.
-        state.startTransition(cToA, transitionKey = null)
+        state.startTransition(cToA)
         assertThat(finishingTransitions).containsExactly(aToB, bToC)
         assertThat(state.finishedTransitions).isEmpty()
         assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
@@ -617,8 +610,8 @@
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
 
         fun startTransition() {
-            val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */} })
-            state.startTransition(transition, transitionKey = null)
+            val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */ } })
+            state.startTransition(transition)
         }
 
         var hasLoggedWtf = false
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 08532bd..a8dd572 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -21,6 +21,7 @@
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
@@ -333,6 +334,42 @@
     }
 
     @Test
+    fun layoutSizeDoesNotOverscrollWhenOverscrollIsSpecified() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions { overscroll(SceneB, Orientation.Horizontal) }
+                )
+            }
+
+        val layoutTag = "layout"
+        rule.setContent {
+            SceneTransitionLayout(state, Modifier.testTag(layoutTag)) {
+                scene(SceneA) { Box(Modifier.size(50.dp)) }
+                scene(SceneB) { Box(Modifier.size(70.dp)) }
+            }
+        }
+
+        // Overscroll on A at -100%: size should be interpolated given that there is no overscroll
+        // defined for scene A.
+        var progress by mutableStateOf(-1f)
+        rule.runOnIdle {
+            state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
+        }
+        rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(30.dp)
+
+        // Middle of the transition.
+        progress = 0.5f
+        rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(60.dp)
+
+        // Overscroll on B at 200%: size should not be interpolated given that there is an
+        // overscroll defined for scene B.
+        progress = 2f
+        rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(70.dp)
+    }
+
+    @Test
     fun multipleTransitionsWillComposeMultipleScenes() {
         val duration = 10 * 16L
 
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
index e6fa69d..322b035 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
@@ -30,7 +30,7 @@
     current: () -> SceneKey = { from },
     progress: () -> Float = { 0f },
     progressVelocity: () -> Float = { 0f },
-    interruptionProgress: () -> Float = { 100f },
+    interruptionProgress: () -> Float = { 0f },
     isInitiatedByUserInput: Boolean = false,
     isUserInputOngoing: Boolean = false,
     isUpOrLeft: Boolean = false,
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
index e743c78..6d063a0 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt
@@ -17,7 +17,7 @@
 package com.android.compose.animation.scene
 
 import androidx.compose.ui.test.SemanticsMatcher
-import androidx.compose.ui.test.hasParent
+import androidx.compose.ui.test.hasAnyAncestor
 import androidx.compose.ui.test.hasTestTag
 
 /** A [SemanticsMatcher] that matches [element], optionally restricted to scene [scene]. */
@@ -25,6 +25,6 @@
     return if (scene == null) {
         hasTestTag(element.testTag)
     } else {
-        hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag))
+        hasTestTag(element.testTag) and hasAnyAncestor(hasTestTag(scene.testTag))
     }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 079c1d9..1863cd8 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -531,8 +531,10 @@
         moveFraction: Float,
     ) {
         val isMovingToCenter = if (isLayoutRtl) clockMoveDirection < 0 else clockMoveDirection > 0
+        // The sign of moveAmountDeltaForDigit is already set here
+        // we can interpret (left - clockStartLeft) as (destinationPosition - originPosition)
+        // so we no longer need to multiply direct sign to moveAmountDeltaForDigit
         val currentMoveAmount = left - clockStartLeft
-        val digitOffsetDirection = if (isLayoutRtl) -1 else 1
         for (i in 0 until NUM_DIGITS) {
             val digitFraction =
                 getDigitFraction(
@@ -542,7 +544,7 @@
                 )
             val moveAmountForDigit = currentMoveAmount * digitFraction
             val moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount
-            glyphOffsets[i] = digitOffsetDirection * moveAmountDeltaForDigit
+            glyphOffsets[i] = moveAmountDeltaForDigit
         }
         invalidate()
     }
@@ -582,6 +584,17 @@
         invalidate()
     }
 
+    override fun onRtlPropertiesChanged(layoutDirection: Int) {
+        if (migratedClocks) {
+            if (layoutDirection == LAYOUT_DIRECTION_RTL) {
+                textAlignment = TEXT_ALIGNMENT_TEXT_END
+            } else {
+                textAlignment = TEXT_ALIGNMENT_TEXT_START
+            }
+        }
+        super.onRtlPropertiesChanged(layoutDirection)
+    }
+
     private fun getDigitFraction(digit: Int, isMovingToCenter: Boolean, fraction: Float): Float {
         // The delay for the digit, in terms of fraction.
         // (i.e. the digit should not move during 0.0 - 0.1).
diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py
index 95a25c5..d78ef5a 100755
--- a/packages/SystemUI/flag_check.py
+++ b/packages/SystemUI/flag_check.py
@@ -52,7 +52,7 @@
         nargs='?',
         default='',
         help=
-        'REPO_PATH in repo upload to determine whether the check should run for this project.')
+        'REPO_PROJECT in repo upload to determine whether the check should run for this project.')
 
     # Parse the arguments
     args = parser.parse_args()
@@ -112,16 +112,16 @@
     sys.exit(0)
 
 
-def should_run_path(path, files):
+def should_run_path(project, files):
     """Returns a boolean if this check should run with these paths.
     If you want to check for a particular subdirectory under the path,
     add a check here, call should_run_files and check for a specific sub dir path in should_run_files.
     """
-    if not path:
+    if not project:
         return False
-    if path == 'frameworks/base':
+    if project == 'platform/frameworks/base':
         return should_run_files(files)
-    # Default case, run for all other paths which calls this script.
+    # Default case, run for all other projects which calls this script.
     return True
 
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
similarity index 87%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
index f561c53..201ed00 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
 
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
@@ -44,6 +44,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.core.FakeLogBuffer;
 import com.android.systemui.res.R;
@@ -54,7 +58,6 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
-import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.time.DateFormatUtil;
 
 import org.junit.Before;
@@ -72,14 +75,12 @@
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidJUnit4.class)
-public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+public class AmbientStatusBarViewControllerTest extends SysuiTestCase {
     private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING =
             "{count, plural, =1 {# notification} other {# notifications}}";
 
     @Mock
-    MockDreamOverlayStatusBarView mView;
-    @Mock
-    TouchInsetManager.TouchInsetSession mTouchSession;
+    MockAmbientStatusBarView mView;
     @Mock
     Resources mResources;
     @Mock
@@ -114,9 +115,11 @@
 
     private final Executor mMainExecutor = Runnable::run;
 
-    private final FakeWifiRepository mWifiRepository = new FakeWifiRepository();
+    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
 
-    DreamOverlayStatusBarViewController mController;
+    private final FakeWifiRepository mWifiRepository = mKosmos.getFakeWifiRepository();
+
+    AmbientStatusBarViewController mController;
 
     @Before
     public void setup() {
@@ -128,11 +131,10 @@
         doCallRealMethod().when(mView).getVisibility();
         when(mUserTracker.getUserId()).thenReturn(ActivityManager.getCurrentUser());
 
-        mController = new DreamOverlayStatusBarViewController(
+        mController = new AmbientStatusBarViewController(
                 mView,
                 mResources,
                 mMainExecutor,
-                mTouchSession,
                 mAlarmManager,
                 mNextAlarmController,
                 mDateFormatUtil,
@@ -143,7 +145,8 @@
                 mDreamOverlayStatusBarItemsProvider,
                 mDreamOverlayStateController,
                 mUserTracker,
-                mWifiRepository,
+                mKosmos.getWifiInteractor(),
+                mKosmos.getCommunalSceneInteractor(),
                 mLogBuffer);
     }
 
@@ -164,7 +167,7 @@
         mController.updateWifiUnavailableStatusIcon(false);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
+                AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null);
     }
 
     @Test
@@ -173,7 +176,7 @@
         mController.updateWifiUnavailableStatusIcon(true);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
+                AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null);
     }
 
     @Test
@@ -183,7 +186,7 @@
         when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(alarmClockInfo);
         mController.onViewAttached();
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any());
+                eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any());
     }
 
     @Test
@@ -191,7 +194,7 @@
         when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(null);
         mController.onViewAttached();
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull());
+                eq(AmbientStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull());
     }
 
     @Test
@@ -202,7 +205,7 @@
                 .thenReturn(false);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, true, null);
+                AmbientStatusBarView.STATUS_ICON_MIC_DISABLED, true, null);
     }
 
     @Test
@@ -213,7 +216,7 @@
                 .thenReturn(true);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null);
+                AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null);
     }
 
     @Test
@@ -224,7 +227,7 @@
                 .thenReturn(true);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
+                AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
     }
 
     @Test
@@ -237,7 +240,7 @@
         callbackCapture.getValue().onNotificationCountChanged(1);
 
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
     }
 
     @Test
@@ -250,16 +253,15 @@
         callbackCapture.getValue().onNotificationCountChanged(0);
 
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull());
     }
 
     @Test
     public void testNotificationsIconNotShownWhenCountProviderAbsent() {
-        DreamOverlayStatusBarViewController controller = new DreamOverlayStatusBarViewController(
+        AmbientStatusBarViewController controller = new AmbientStatusBarViewController(
                 mView,
                 mResources,
                 mMainExecutor,
-                mTouchSession,
                 mAlarmManager,
                 mNextAlarmController,
                 mDateFormatUtil,
@@ -270,11 +272,12 @@
                 mDreamOverlayStatusBarItemsProvider,
                 mDreamOverlayStateController,
                 mUserTracker,
-                mWifiRepository,
+                mKosmos.getWifiInteractor(),
+                mKosmos.getCommunalSceneInteractor(),
                 mLogBuffer);
         controller.onViewAttached();
         verify(mView, never()).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
     }
 
     @Test
@@ -283,7 +286,7 @@
                 Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
     }
 
     @Test
@@ -292,7 +295,7 @@
                 Settings.Global.ZEN_MODE_OFF);
         mController.onViewAttached();
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
     }
 
     @Test
@@ -322,7 +325,7 @@
         callbackCapture.getValue().onNotificationCountChanged(1);
 
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
     }
 
     @Test
@@ -335,7 +338,7 @@
         callbackCapture.getValue().onNotificationCountChanged(0);
 
         verify(mView).showIcon(
-                eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any());
+                eq(AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any());
     }
 
     @Test
@@ -354,7 +357,7 @@
                 SensorPrivacyManager.Sensors.MICROPHONE, true);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
+                AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null);
     }
 
     @Test
@@ -369,7 +372,7 @@
         callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null);
     }
 
     @Test
@@ -384,7 +387,7 @@
         callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF);
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null);
     }
 
     @Test
@@ -399,7 +402,7 @@
         callbackCapture.getValue().onStateChanged();
 
         verify(mView).showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null);
+                AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE, true, null);
     }
 
     @Test
@@ -460,7 +463,7 @@
 
         final ArgumentCaptor<DreamOverlayStatusBarItemsProvider.Callback>
                 callbackCapture = ArgumentCaptor.forClass(
-                        DreamOverlayStatusBarItemsProvider.Callback.class);
+                DreamOverlayStatusBarItemsProvider.Callback.class);
         verify(mDreamOverlayStatusBarItemsProvider).addCallback(callbackCapture.capture());
         callbackCapture.getValue().onStatusBarItemsChanged(List.of(mStatusBarItem));
 
@@ -532,10 +535,10 @@
         callback.onStateChanged();
     }
 
-    private static class MockDreamOverlayStatusBarView extends DreamOverlayStatusBarView {
+    private static class MockAmbientStatusBarView extends AmbientStatusBarView {
         private int mVisibility = View.VISIBLE;
 
-        private MockDreamOverlayStatusBarView(Context context) {
+        private MockAmbientStatusBarView(Context context) {
             super(context);
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
index 27bffd0..07d8890 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
@@ -18,15 +18,22 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.DreamManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
 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.shade.ShadeViewController;
 import com.android.systemui.shared.system.InputChannelCompat;
@@ -36,6 +43,7 @@
 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.MockitoAnnotations;
@@ -52,63 +60,128 @@
     ShadeViewController mShadeViewController;
 
     @Mock
+    DreamManager mDreamManager;
+
+    @Mock
     TouchHandler.TouchSession mTouchSession;
 
     ShadeTouchHandler mTouchHandler;
 
+    @Captor
+    ArgumentCaptor<GestureDetector.OnGestureListener> mGestureListenerCaptor;
+    @Captor
+    ArgumentCaptor<InputChannelCompat.InputEventListener> mInputListenerCaptor;
+
     private static final int TOUCH_HEIGHT = 20;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+
         mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), mShadeViewController,
-                TOUCH_HEIGHT);
+                mDreamManager, TOUCH_HEIGHT);
+    }
+
+    // Verifies that a swipe down in the gesture region is captured by the shade touch handler.
+    @Test
+    public void testSwipeDown_captured() {
+        final boolean captured = swipe(Direction.DOWN);
+
+        assertThat(captured).isTrue();
+    }
+
+    // Verifies that a swipe in the upward direction is not captured.
+    @Test
+    public void testSwipeUp_notCaptured() {
+        final boolean captured = swipe(Direction.UP);
+
+        // Motion events not captured as the swipe is going in the wrong direction.
+        assertThat(captured).isFalse();
+    }
+
+    // Verifies that a swipe down forwards captured touches to central surfaces for handling.
+    @Test
+    @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+    public void testSwipeDown_communalEnabled_sentToCentralSurfaces() {
+        swipe(Direction.DOWN);
+
+        // Both motion events are sent for central surfaces to process.
+        verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any());
+    }
+
+    // Verifies that a swipe down forwards captured touches to the shade view for handling.
+    @Test
+    @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+    public void testSwipeDown_communalDisabled_sentToShadeView() {
+        swipe(Direction.DOWN);
+
+        // Both motion events are sent for the shade view to process.
+        verify(mShadeViewController, times(2)).handleExternalTouch(any());
+    }
+
+    // Verifies that a swipe down while dreaming forwards captured touches to the shade view for
+    // handling.
+    @Test
+    public void testSwipeDown_dreaming_sentToShadeView() {
+        when(mDreamManager.isDreaming()).thenReturn(true);
+
+        swipe(Direction.DOWN);
+
+        // Both motion events are sent for the shade view to process.
+        verify(mShadeViewController, times(2)).handleExternalTouch(any());
+    }
+
+    // Verifies that a swipe up is not forwarded to central surfaces.
+    @Test
+    @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+    public void testSwipeUp_communalEnabled_touchesNotSent() {
+        swipe(Direction.UP);
+
+        // Motion events are not sent for central surfaces to process as the swipe is going in the
+        // wrong direction.
+        verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any());
+    }
+
+    // Verifies that a swipe up is not forwarded to the shade view.
+    @Test
+    @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+    public void testSwipeUp_communalDisabled_touchesNotSent() {
+        swipe(Direction.UP);
+
+        // Motion events are not sent for the shade view to process as the swipe is going in the
+        // wrong direction.
+        verify(mShadeViewController, never()).handleExternalTouch(any());
     }
 
     /**
-     * Verify that touches aren't handled when the bouncer is showing.
+     * Simulates a swipe in the given direction and returns true if the touch was intercepted by the
+     * touch handler's gesture listener.
+     * <p>
+     * Swipe down starts from a Y coordinate of 0 and goes downward. Swipe up starts from the edge
+     * of the gesture region, {@link #TOUCH_HEIGHT}, and goes upward to 0.
      */
-    @Test
-    public void testInactiveOnBouncer() {
-        when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+    private boolean swipe(Direction direction) {
+        Mockito.clearInvocations(mTouchSession);
         mTouchHandler.onSessionStart(mTouchSession);
-        verify(mTouchSession).pop();
+
+        verify(mTouchSession).registerGestureListener(mGestureListenerCaptor.capture());
+        verify(mTouchSession).registerInputListener(mInputListenerCaptor.capture());
+
+        final float startY = direction == Direction.UP ? TOUCH_HEIGHT : 0;
+        final float endY = direction == Direction.UP ? 0 : TOUCH_HEIGHT;
+
+        // Send touches to the input and gesture listener.
+        final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, startY, 0);
+        final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, endY, 0);
+        mInputListenerCaptor.getValue().onInputEvent(event1);
+        mInputListenerCaptor.getValue().onInputEvent(event2);
+        final boolean captured = mGestureListenerCaptor.getValue().onScroll(event1, event2, 0,
+                startY - endY);
+
+        return captured;
     }
 
-    /**
-     * Make sure {@link ShadeTouchHandler}
-     */
-    @Test
-    public void testTouchPilferingOnScroll() {
-        final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class);
-        final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class);
-
-        final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor =
-                ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
-
-        mTouchHandler.onSessionStart(mTouchSession);
-        verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture());
-
-        assertThat(gestureListenerArgumentCaptor.getValue()
-                .onScroll(motionEvent1, motionEvent2, 1, 1))
-                .isTrue();
+    private enum Direction {
+        DOWN, UP,
     }
-
-    /**
-     * Ensure touches are propagated to the {@link ShadeViewController}.
-     */
-    @Test
-    public void testEventPropagation() {
-        final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
-
-        final ArgumentCaptor<InputChannelCompat.InputEventListener>
-                inputEventListenerArgumentCaptor =
-                    ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
-
-        mTouchHandler.onSessionStart(mTouchSession);
-        verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
-        inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
-        verify(mShadeViewController).handleExternalTouch(motionEvent);
-    }
-
 }
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 9c2791f..75a77cf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -131,8 +131,9 @@
     negativeButton: String = "neg",
 ): PromptInfo {
     val info = PromptInfo()
-    info.logoRes = logoRes
-    info.logoBitmap = logoBitmap
+    if (logoBitmap != null) {
+        info.setLogo(logoRes, logoBitmap)
+    }
     info.logoDescription = logoDescription
     info.title = title
     info.subtitle = subtitle
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 f3de463e..fa79ea0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -256,8 +256,6 @@
     @Mock
     private ViewRootImpl mViewRootImpl;
     @Mock
-    private FpsUnlockTracker mFpsUnlockTracker;
-    @Mock
     private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     @Mock
     private Lazy<DeviceEntryUdfpsTouchOverlayViewModel> mDeviceEntryUdfpsTouchOverlayViewModel;
@@ -368,7 +366,6 @@
                 mock(DeviceEntryFaceAuthInteractor.class),
                 mUdfpsKeyguardAccessibilityDelegate,
                 mSelectedUserInteractor,
-                mFpsUnlockTracker,
                 mKeyguardTransitionInteractor,
                 mDeviceEntryUdfpsTouchOverlayViewModel,
                 mDefaultUdfpsTouchOverlayViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index ab55125..29a6e56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -235,6 +235,7 @@
 
             job.cancel()
         }
+
     @Test
     fun fadeFromDialogSuggestedAlpha() =
         testScope.runTest {
@@ -511,9 +512,10 @@
         testScope.runTest {
             // GIVEN view is attached
             mController.onViewAttached()
+            val job = mController.listenForLockscreenAodTransitions(this)
+            runCurrent()
             Mockito.reset(mView)
 
-            val job = mController.listenForLockscreenAodTransitions(this)
             // WHEN aod to lockscreen transition is cancelled
             transitionRepository.sendTransitionStep(
                 TransitionStep(
@@ -537,7 +539,7 @@
 
             // THEN doze amount is updated to zero
             verify(mView)
-                .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE))
+                .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN))
             job.cancel()
         }
 
@@ -546,9 +548,10 @@
         testScope.runTest {
             // GIVEN view is attached
             mController.onViewAttached()
+            val job = mController.listenForLockscreenAodTransitions(this)
+            runCurrent()
             Mockito.reset(mView)
 
-            val job = mController.listenForLockscreenAodTransitions(this)
             // WHEN lockscreen to aod transition is cancelled
             transitionRepository.sendTransitionStep(
                 TransitionStep(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
index bfed33c..fe683e07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
@@ -22,7 +22,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardRepository
@@ -66,7 +65,6 @@
                     powerInteractor = kosmos.powerInteractor,
                     keyguardInteractor = kosmos.keyguardInteractor,
                     keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
-                    communalInteractor = kosmos.communalInteractor,
                     dreamManager = dreamManager,
                     bgScope = kosmos.applicationCoroutineScope,
                 )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index e61b2d0..cf14547 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -111,6 +111,25 @@
             }
         }
 
+    @Test
+    fun keyguardGoesAway_whenInEditMode_doesNotChangeScene() =
+        with(kosmos) {
+            testScope.runTest {
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+                communalInteractor.setEditModeOpen(true)
+                fakeKeyguardTransitionRepository.sendTransitionSteps(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = KeyguardState.GONE,
+                    testScope = this
+                )
+                // Scene change will be handled in EditWidgetsActivity not here
+                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            }
+        }
+
     @Ignore("Ignored until custom animations are implemented in b/322787129")
     @Test
     fun deviceDocked_forceCommunalScene() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index e42a67b..3d454a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -52,6 +52,7 @@
 import com.android.systemui.communal.shared.model.CommunalContentSize
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
@@ -121,6 +122,7 @@
     private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
     private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
     private lateinit var sceneInteractor: SceneInteractor
+    private lateinit var communalSceneInteractor: CommunalSceneInteractor
     private lateinit var userTracker: FakeUserTracker
     private lateinit var activityStarter: ActivityStarter
     private lateinit var userManager: UserManager
@@ -141,6 +143,7 @@
         editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
         communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
         sceneInteractor = kosmos.sceneInteractor
+        communalSceneInteractor = kosmos.communalSceneInteractor
         userTracker = kosmos.fakeUserTracker
         activityStarter = kosmos.activityStarter
         userManager = kosmos.userManager
@@ -815,7 +818,11 @@
     @Test
     fun testShowWidgetEditorStartsActivity() =
         testScope.runTest {
+            val editModeState by collectLastValue(communalSceneInteractor.editModeState)
+
             underTest.showWidgetEditor()
+
+            assertThat(editModeState).isEqualTo(EditModeState.STARTING)
             verify(editWidgetsActivityStarter).startActivity()
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
index aad2e60..43293c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -20,15 +20,19 @@
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.communal.data.repository.communalSceneRepository
 import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
 import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.coroutines.collectLastValue
 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.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -63,6 +67,45 @@
             assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
         }
 
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun snapToSceneWithDelay() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+            underTest.snapToScene(
+                CommunalScenes.Communal,
+                ActivityTransitionAnimator.TIMINGS.totalDuration
+            )
+            assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+            advanceTimeBy(ActivityTransitionAnimator.TIMINGS.totalDuration)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+        }
+
+    @Test
+    fun changeSceneForActivityStartOnDismissKeyguard() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            underTest.snapToScene(CommunalScenes.Communal)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+
+            underTest.changeSceneForActivityStartOnDismissKeyguard()
+            assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+        }
+
+    @Test
+    fun changeSceneForActivityStartOnDismissKeyguard_willNotChangeScene_forEditModeActivity() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            underTest.snapToScene(CommunalScenes.Communal)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+
+            underTest.setEditModeState(EditModeState.STARTING)
+
+            underTest.changeSceneForActivityStartOnDismissKeyguard()
+            assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+        }
+
     @Test
     fun transitionProgress_fullProgress() =
         testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
new file mode 100644
index 0000000..0cd3fb2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandlerTest.kt
@@ -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 com.android.systemui.communal.smartspace
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.RemoteViews.RemoteResponse
+import androidx.core.util.component1
+import androidx.core.util.component2
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
+import com.android.systemui.plugins.ActivityStarter
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.notNull
+import org.mockito.kotlin.refEq
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SmartspaceInteractionHandlerTest : SysuiTestCase() {
+    private val activityStarter = mock<ActivityStarter>()
+
+    private val testIntent =
+        PendingIntent.getActivity(
+            context,
+            /* requestCode = */ 0,
+            Intent("action"),
+            PendingIntent.FLAG_IMMUTABLE
+        )
+    private val testResponse = RemoteResponse.fromPendingIntent(testIntent)
+
+    private val underTest: SmartspaceInteractionHandler by lazy {
+        SmartspaceInteractionHandler(activityStarter)
+    }
+
+    @Test
+    fun launchAnimatorIsUsedForSmartspaceView() {
+        val parent = FrameLayout(context)
+        val view = SmartspaceAppWidgetHostView(context)
+        parent.addView(view)
+        val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
+
+        underTest.onInteraction(view, testIntent, testResponse)
+
+        // Verify that we pass in a non-null animation controller
+        verify(activityStarter)
+            .startPendingIntentWithoutDismissing(
+                /* intent = */ eq(testIntent),
+                /* dismissShade = */ eq(false),
+                /* intentSentUiThreadCallback = */ isNull(),
+                /* animationController = */ notNull(),
+                /* fillInIntent = */ refEq(fillInIntent),
+                /* extraOptions = */ refEq(activityOptions.toBundle()),
+            )
+    }
+
+    @Test
+    fun launchAnimatorIsNotUsedForRegularView() {
+        val parent = FrameLayout(context)
+        val view = View(context)
+        parent.addView(view)
+        val (fillInIntent, activityOptions) = testResponse.getLaunchOptions(view)
+
+        underTest.onInteraction(view, testIntent, testResponse)
+
+        // Verify null is used as the animation controller
+        verify(activityStarter)
+            .startPendingIntentWithoutDismissing(
+                /* intent = */ eq(testIntent),
+                /* dismissShade = */ eq(false),
+                /* intentSentUiThreadCallback = */ isNull(),
+                /* animationController = */ isNull(),
+                /* fillInIntent = */ refEq(fillInIntent),
+                /* extraOptions = */ refEq(activityOptions.toBundle()),
+            )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
index f9d5073..baeb2dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelTest.kt
@@ -19,50 +19,45 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 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.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class CommunalTransitionViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
-    val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val communalSceneRepository = kosmos.fakeCommunalSceneRepository
 
-    private lateinit var underTest: CommunalTransitionViewModel
-
-    @Before
-    fun setup() {
-        underTest = kosmos.communalTransitionViewModel
+    private val underTest: CommunalTransitionViewModel by lazy {
+        kosmos.communalTransitionViewModel
     }
 
     @Test
     fun testIsUmoOnCommunalDuringTransitionBetweenLockscreenAndGlanceableHub() =
         testScope.runTest {
             val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
-            assertThat(isUmoOnCommunal).isNull()
+            runCurrent()
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.GLANCEABLE_HUB,
-                testScope
-            )
+            enterCommunal(from = KeyguardState.LOCKSCREEN)
             assertThat(isUmoOnCommunal).isTrue()
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.GLANCEABLE_HUB,
-                to = KeyguardState.LOCKSCREEN,
-                testScope
-            )
+            exitCommunal(to = KeyguardState.LOCKSCREEN)
             assertThat(isUmoOnCommunal).isFalse()
         }
 
@@ -70,20 +65,12 @@
     fun testIsUmoOnCommunalDuringTransitionBetweenDreamingAndGlanceableHub() =
         testScope.runTest {
             val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
-            assertThat(isUmoOnCommunal).isNull()
+            runCurrent()
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.DREAMING,
-                to = KeyguardState.GLANCEABLE_HUB,
-                testScope
-            )
+            enterCommunal(from = KeyguardState.DREAMING)
             assertThat(isUmoOnCommunal).isTrue()
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.GLANCEABLE_HUB,
-                to = KeyguardState.DREAMING,
-                testScope
-            )
+            exitCommunal(to = KeyguardState.DREAMING)
             assertThat(isUmoOnCommunal).isFalse()
         }
 
@@ -91,21 +78,61 @@
     fun testIsUmoOnCommunalDuringTransitionBetweenOccludedAndGlanceableHub() =
         testScope.runTest {
             val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
-            assertThat(isUmoOnCommunal).isNull()
+            runCurrent()
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.OCCLUDED,
-                to = KeyguardState.GLANCEABLE_HUB,
-                testScope
-            )
+            enterCommunal(from = KeyguardState.OCCLUDED)
             assertThat(isUmoOnCommunal).isTrue()
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.GLANCEABLE_HUB,
-                to = KeyguardState.OCCLUDED,
-                testScope
-            )
-
+            exitCommunal(to = KeyguardState.OCCLUDED)
             assertThat(isUmoOnCommunal).isFalse()
         }
+
+    @Test
+    fun isUmoOnCommunal_noLongerVisible_returnsFalse() =
+        testScope.runTest {
+            val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
+            runCurrent()
+
+            enterCommunal(from = KeyguardState.LOCKSCREEN)
+            assertThat(isUmoOnCommunal).isTrue()
+
+            // Communal is no longer visible.
+            communalSceneRepository.changeScene(CommunalScenes.Blank)
+
+            // isUmoOnCommunal returns false, even without any keyguard transition.
+            assertThat(isUmoOnCommunal).isFalse()
+        }
+
+    @Test
+    fun isUmoOnCommunal_idleOnCommunal_returnsTrue() =
+        testScope.runTest {
+            val isUmoOnCommunal by collectLastValue(underTest.isUmoOnCommunal)
+            assertThat(isUmoOnCommunal).isFalse()
+
+            // Communal is fully visible.
+            communalSceneRepository.changeScene(CommunalScenes.Communal)
+
+            // isUmoOnCommunal returns true, even without any keyguard transition.
+            assertThat(isUmoOnCommunal).isTrue()
+        }
+
+    private suspend fun TestScope.enterCommunal(from: KeyguardState) {
+        keyguardTransitionRepository.sendTransitionSteps(
+            from = from,
+            to = KeyguardState.GLANCEABLE_HUB,
+            testScope
+        )
+        communalSceneRepository.changeScene(CommunalScenes.Communal)
+        runCurrent()
+    }
+
+    private suspend fun TestScope.exitCommunal(to: KeyguardState) {
+        keyguardTransitionRepository.sendTransitionSteps(
+            from = KeyguardState.GLANCEABLE_HUB,
+            to = to,
+            testScope
+        )
+        communalSceneRepository.changeScene(CommunalScenes.Blank)
+        runCurrent()
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 84dbfd4..d5fe2a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -38,14 +38,17 @@
 import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 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.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
@@ -86,6 +89,7 @@
     private lateinit var widgetRepository: FakeCommunalWidgetRepository
     private lateinit var smartspaceRepository: FakeSmartspaceRepository
     private lateinit var mediaRepository: FakeCommunalMediaRepository
+    private lateinit var communalSceneInteractor: CommunalSceneInteractor
 
     private val testableResources = context.orCreateTestableResources
 
@@ -99,6 +103,7 @@
         widgetRepository = kosmos.fakeCommunalWidgetRepository
         smartspaceRepository = kosmos.fakeSmartspaceRepository
         mediaRepository = kosmos.fakeCommunalMediaRepository
+        communalSceneInteractor = kosmos.communalSceneInteractor
         kosmos.fakeUserTracker.set(
             userInfos = listOf(MAIN_USER_INFO),
             selectedUserIndex = 0,
@@ -107,9 +112,10 @@
 
         underTest =
             CommunalEditModeViewModel(
-                kosmos.communalSceneInteractor,
+                communalSceneInteractor,
                 kosmos.communalInteractor,
                 kosmos.communalSettingsInteractor,
+                kosmos.keyguardTransitionInteractor,
                 mediaHost,
                 uiEventLogger,
                 logcatLogBuffer("CommunalEditModeViewModelTest"),
@@ -172,6 +178,22 @@
         }
 
     @Test
+    fun isCommunalContentVisible_isTrue_whenEditModeShowing() =
+        testScope.runTest {
+            val isCommunalContentVisible by collectLastValue(underTest.isCommunalContentVisible)
+            communalSceneInteractor.setEditModeState(EditModeState.SHOWING)
+            assertThat(isCommunalContentVisible).isEqualTo(true)
+        }
+
+    @Test
+    fun isCommunalContentVisible_isFalse_whenEditModeNotShowing() =
+        testScope.runTest {
+            val isCommunalContentVisible by collectLastValue(underTest.isCommunalContentVisible)
+            communalSceneInteractor.setEditModeState(null)
+            assertThat(isCommunalContentVisible).isEqualTo(false)
+        }
+
+    @Test
     fun deleteWidget() =
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
index 420b11c..7b7d03b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -27,23 +27,19 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.util.mockito.eq
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.refEq
-import org.mockito.Mock
-import org.mockito.Mockito.isNull
-import org.mockito.Mockito.notNull
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.notNull
+import org.mockito.kotlin.refEq
+import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class WidgetInteractionHandlerTest : SysuiTestCase() {
-    @Mock private lateinit var activityStarter: ActivityStarter
-
-    private lateinit var underTest: WidgetInteractionHandler
+    private val activityStarter = mock<ActivityStarter>()
 
     private val testIntent =
         PendingIntent.getActivity(
@@ -54,10 +50,8 @@
         )
     private val testResponse = RemoteResponse.fromPendingIntent(testIntent)
 
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        underTest = WidgetInteractionHandler(activityStarter)
+    private val underTest: WidgetInteractionHandler by lazy {
+        WidgetInteractionHandler(activityStarter)
     }
 
     @Test
@@ -69,14 +63,15 @@
 
         underTest.onInteraction(view, testIntent, testResponse)
 
+        // Verify that we pass in a non-null animation controller
         verify(activityStarter)
             .startPendingIntentMaybeDismissingKeyguard(
-                eq(testIntent),
-                eq(false),
-                isNull(),
-                notNull(),
-                refEq(fillInIntent),
-                refEq(activityOptions.toBundle()),
+                /* intent = */ eq(testIntent),
+                /* dismissShade = */ eq(false),
+                /* intentSentUiThreadCallback = */ isNull(),
+                /* animationController = */ notNull(),
+                /* fillInIntent = */ refEq(fillInIntent),
+                /* extraOptions = */ refEq(activityOptions.toBundle()),
             )
     }
 
@@ -89,14 +84,15 @@
 
         underTest.onInteraction(view, testIntent, testResponse)
 
+        // Verify null is used as the animation controller
         verify(activityStarter)
             .startPendingIntentMaybeDismissingKeyguard(
-                eq(testIntent),
-                eq(false),
-                isNull(),
-                isNull(),
-                refEq(fillInIntent),
-                refEq(activityOptions.toBundle()),
+                /* intent = */ eq(testIntent),
+                /* dismissShade = */ eq(false),
+                /* intentSentUiThreadCallback = */ isNull(),
+                /* animationController = */ isNull(),
+                /* fillInIntent = */ refEq(fillInIntent),
+                /* extraOptions = */ refEq(activityOptions.toBundle()),
             )
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryTest.kt
new file mode 100644
index 0000000..79115ae
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryTest.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.deviceconfig.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.fakeDeviceConfigProxy
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceConfigRepositoryTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val dataSource = kosmos.fakeDeviceConfigProxy
+
+    private val underTest = kosmos.deviceConfigRepository
+
+    @Test
+    fun booleanProperty() =
+        testScope.runTest {
+            val property by collectLastValue(underTest.property("namespace", "name", false))
+            assertThat(property).isFalse()
+
+            dataSource.setProperty("namespace", "name", "true", /* makeDefault= */ false)
+            kosmos.fakeExecutor.runAllReady()
+            assertThat(property).isTrue()
+
+            dataSource.setProperty("namespace", "name", "false", /* makeDefault= */ false)
+            kosmos.fakeExecutor.runAllReady()
+            assertThat(property).isFalse()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorTest.kt
new file mode 100644
index 0000000..6ec4cea
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorTest.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.deviceconfig.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.fakeDeviceConfigProxy
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceConfigInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val dataSource = kosmos.fakeDeviceConfigProxy
+
+    private val underTest = kosmos.deviceConfigInteractor
+
+    @Test
+    fun booleanProperty() =
+        testScope.runTest {
+            val property by collectLastValue(underTest.property("namespace", "name", false))
+            assertThat(property).isFalse()
+
+            dataSource.setProperty("namespace", "name", "true", /* makeDefault= */ false)
+            kosmos.fakeExecutor.runAllReady()
+            assertThat(property).isTrue()
+
+            dataSource.setProperty("namespace", "name", "false", /* makeDefault= */ false)
+            kosmos.fakeExecutor.runAllReady()
+            assertThat(property).isFalse()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 86fdaa5..73ef775 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -7,6 +7,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
 import com.android.systemui.complication.ComplicationHostViewController
 import com.android.systemui.dreams.ui.viewmodel.DreamViewModel
 import com.android.systemui.log.core.FakeLogBuffer
@@ -43,7 +44,7 @@
     @Mock private lateinit var mockAnimator: AnimatorSet
     @Mock private lateinit var blurUtils: BlurUtils
     @Mock private lateinit var hostViewController: ComplicationHostViewController
-    @Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController
+    @Mock private lateinit var statusBarViewController: AmbientStatusBarViewController
     @Mock private lateinit var stateController: DreamOverlayStateController
     @Mock private lateinit var transitionViewModel: DreamViewModel
     private val logBuffer = FakeLogBuffer.Factory.create()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index f5c86e0..c48ced1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams;
 
 import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -47,6 +48,7 @@
 import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
 import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
@@ -55,6 +57,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.touch.TouchInsetManager;
 
 import kotlinx.coroutines.CoroutineDispatcher;
 
@@ -80,7 +83,7 @@
     ViewTreeObserver mViewTreeObserver;
 
     @Mock
-    DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
+    AmbientStatusBarViewController mAmbientStatusBarViewController;
 
     @Mock
     LowLightTransitionCoordinator mLowLightTransitionCoordinator;
@@ -131,6 +134,8 @@
     CommunalInteractor mCommunalInteractor;
     @Mock
     private DreamManager mDreamManager;
+    @Mock
+    private TouchInsetManager.TouchInsetSession mTouchInsetSession;
 
     DreamOverlayContainerViewController mController;
 
@@ -144,14 +149,17 @@
         when(mDreamOverlayContainerView.getRootSurfaceControl())
                 .thenReturn(mAttachedSurfaceControl);
         when(mKeyguardTransitionInteractor.isFinishedInStateWhere(any())).thenReturn(emptyFlow());
+        when(mShadeInteractor.isAnyExpanded()).thenReturn(MutableStateFlow(false));
+        when(mCommunalInteractor.isCommunalShowing()).thenReturn(MutableStateFlow(false));
 
         mController = new DreamOverlayContainerViewController(
                 mDreamOverlayContainerView,
                 mComplicationHostViewController,
                 mDreamOverlayContentView,
                 mHubGestureIndicatorView,
-                mDreamOverlayStatusBarViewController,
+                mAmbientStatusBarViewController,
                 mLowLightTransitionCoordinator,
+                mTouchInsetSession,
                 mBlurUtils,
                 mHandler,
                 mDispatcher,
@@ -190,7 +198,7 @@
     @Test
     public void testDreamOverlayStatusBarViewControllerInitialized() {
         mController.init();
-        verify(mDreamOverlayStatusBarViewController).init();
+        verify(mAmbientStatusBarViewController).init();
     }
 
     @Test
@@ -325,4 +333,12 @@
         mController.onViewDetached();
         verify(mBouncerlessScrimController).removeCallback(any());
     }
+
+    @EnableFlags(android.service.dreams.Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED)
+    @Test
+    public void testOnViewAttachedSucceedsWhenDreamHandlesBeingObscuredFlagEnabled() {
+        // This test will catch failures in presubmit when the dream_handles_being_obscured flag is
+        // enabled.
+        mController.onViewAttached();
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index ee8a22c..5a39de8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -778,6 +778,7 @@
             DREAM_COMPONENT,
             false /*shouldShowComplication*/
         )
+        testScope.runCurrent()
         mMainExecutor.runAllReady()
         assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index c51413a..74eee9b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -21,26 +21,35 @@
 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.haptics.vibratorHelper
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.statusbar.policy.keyguardStateController
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
 class QSLongPressEffectTest : SysuiTestCase() {
 
+    @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
     private val kosmos = testKosmos()
     private val vibratorHelper = kosmos.vibratorHelper
+    private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
+    @Mock private lateinit var callback: QSLongPressEffect.Callback
 
     private val effectDuration = 400
     private val lowTickDuration = 12
@@ -54,13 +63,15 @@
             lowTickDuration
         vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_SPIN] = spinDuration
 
-        kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
+        whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true)
 
         longPressEffect =
             QSLongPressEffect(
                 vibratorHelper,
-                kosmos.keyguardInteractor,
+                kosmos.keyguardStateController,
             )
+        longPressEffect.callback = callback
+        longPressEffect.qsTile = qsTile
     }
 
     @Test
@@ -107,28 +118,13 @@
         }
 
     @Test
-    fun onActionUp_whileWaiting_performsClick() =
-        testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
-            // GIVEN an action is being collected
-            val action by collectLastValue(longPressEffect.actionType)
-
-            // GIVEN an action up occurs
-            longPressEffect.handleActionUp()
-
-            // THEN the action to invoke is the click action and the effect does not start
-            assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CLICK)
-            assertEffectDidNotStart()
-        }
-
-    @Test
     fun onWaitComplete_whileWaiting_beginsEffect() =
         testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
             // GIVEN the pressed timeout is complete
             longPressEffect.handleTimeoutComplete()
 
             // THEN the effect emits the action to start an animator
-            val action by collectLastValue(longPressEffect.actionType)
-            assertThat(action).isEqualTo(QSLongPressEffect.ActionType.START_ANIMATOR)
+            verify(callback, times(1)).onStartAnimator()
         }
 
     @Test
@@ -148,7 +144,7 @@
             longPressEffect.handleActionUp()
 
             // THEN the effect reverses
-            assertEffectReverses()
+            assertEffectReverses(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_UP)
         }
 
     @Test
@@ -175,30 +171,51 @@
             longPressEffect.handleActionCancel()
 
             // THEN the effect gets reversed
-            assertEffectReverses()
+            assertEffectReverses(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL)
         }
 
     @Test
-    fun onAnimationComplete_keyguardDismissible_effectEndsWithLongPress() =
+    fun onAnimationComplete_keyguardDismissible_effectCompletes() =
         testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) {
             // GIVEN that the animation completes
             longPressEffect.handleAnimationComplete()
 
-            // THEN the long-press effect completes with a LONG_PRESS
-            assertEffectCompleted(QSLongPressEffect.ActionType.LONG_PRESS)
+            // THEN the long-press effect completes
+            assertEffectCompleted()
         }
 
     @Test
-    fun onAnimationComplete_keyguardNotDismissible_effectEndsWithResetAndLongPress() =
+    fun onAnimationComplete_keyguardNotDismissible_effectEndsWithReset() =
         testWhileInState(QSLongPressEffect.State.RUNNING_FORWARD) {
             // GIVEN that the keyguard is not dismissible
-            kosmos.fakeKeyguardRepository.setKeyguardDismissible(false)
+            whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
 
             // GIVEN that the animation completes
             longPressEffect.handleAnimationComplete()
 
-            // THEN the long-press effect completes with RESET_AND_LONG_PRESS
-            assertEffectCompleted(QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS)
+            // THEN the long-press effect completes and the properties are called to reset
+            assertEffectCompleted()
+            verify(callback, times(1)).onResetProperties()
+        }
+
+    @Test
+    fun onAnimationComplete_whenRunningBackwardsFromUp_endsWithFinishedReversing() =
+        testWhileInState(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_UP) {
+            // GIVEN that the animation completes
+            longPressEffect.handleAnimationComplete()
+
+            // THEN the callback for finished reversing is used.
+            verify(callback, times(1)).onEffectFinishedReversing()
+        }
+
+    @Test
+    fun onAnimationComplete_whenRunningBackwardsFromCancel_endsInIdle() =
+        testWhileInState(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL) {
+            // GIVEN that the animation completes
+            longPressEffect.handleAnimationComplete()
+
+            // THEN the effect ends in the idle state.
+            assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
         }
 
     @Test
@@ -211,8 +228,7 @@
             longPressEffect.handleActionDown()
 
             // THEN the effect posts an action to cancel the animator
-            val action by collectLastValue(longPressEffect.actionType)
-            assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CANCEL_ANIMATOR)
+            verify(callback, times(1)).onCancelAnimator()
         }
 
     @Test
@@ -226,11 +242,8 @@
         }
 
     @Test
-    fun onAnimationComplete_whileRunningBackwards_goesToIdle() =
-        testWhileInState(QSLongPressEffect.State.RUNNING_BACKWARDS) {
-            // GIVEN an action cancel occurs and the effect gets reversed
-            longPressEffect.handleActionCancel()
-
+    fun onAnimationComplete_whileRunningBackwardsFromCancel_goesToIdle() =
+        testWhileInState(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL) {
             // GIVEN that the animation completes
             longPressEffect.handleAnimationComplete()
 
@@ -238,6 +251,29 @@
             assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
         }
 
+    @Test
+    fun onTileClick_whileWaiting_withQSTile_clicks() =
+        testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+            // GIVEN that a click was detected
+            val couldClick = longPressEffect.onTileClick()
+
+            // THEN the click is successful
+            assertThat(couldClick).isTrue()
+        }
+
+    @Test
+    fun onTileClick_whileWaiting_withoutQSTile_cannotClick() =
+        testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+            // GIVEN that no QSTile has been set
+            longPressEffect.qsTile = null
+
+            // GIVEN that a click was detected
+            val couldClick = longPressEffect.onTileClick()
+
+            // THEN the click is not successful
+            assertThat(couldClick).isFalse()
+        }
+
     private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
         with(kosmos) {
             testScope.runTest {
@@ -287,12 +323,16 @@
     /**
      * Asserts that the effect did not start by checking that:
      * 1. No haptics are played
-     * 2. The internal state is not [QSLongPressEffect.State.RUNNING_BACKWARDS] or
-     *    [QSLongPressEffect.State.RUNNING_FORWARD]
+     * 2. The internal state is not [QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_UP] or
+     *    [QSLongPressEffect.State.RUNNING_FORWARD] or
+     *    [QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL]
      */
     private fun assertEffectDidNotStart() {
         assertThat(longPressEffect.state).isNotEqualTo(QSLongPressEffect.State.RUNNING_FORWARD)
-        assertThat(longPressEffect.state).isNotEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS)
+        assertThat(longPressEffect.state)
+            .isNotEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_UP)
+        assertThat(longPressEffect.state)
+            .isNotEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL)
         assertThat(vibratorHelper.totalVibrations).isEqualTo(0)
     }
 
@@ -300,27 +340,24 @@
      * Asserts that the effect completes by checking that:
      * 1. The final snap haptics are played
      * 2. The internal state goes back to [QSLongPressEffect.State.IDLE]
-     * 3. The action to perform on the tile is the action given as a parameter
      */
-    private fun TestScope.assertEffectCompleted(expectedAction: QSLongPressEffect.ActionType) {
-        val action by collectLastValue(longPressEffect.actionType)
+    private fun assertEffectCompleted() {
         val snapEffect = LongPressHapticBuilder.createSnapEffect()
 
         assertThat(snapEffect).isNotNull()
         assertThat(vibratorHelper.hasVibratedWithEffects(snapEffect!!)).isTrue()
         assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
-        assertThat(action).isEqualTo(expectedAction)
     }
 
     /**
-     * Assert that the effect gets reverted by checking that:
-     * 1. The internal state is [QSLongPressEffect.State.RUNNING_BACKWARDS]
-     * 2. An action to reverse the animator is emitted
+     * Assert that the effect gets reverted by checking that the callback to reverse the animator is
+     * used, and that the state is given reversing state.
+     *
+     * @param[reversingState] Either [QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_CANCEL] or
+     *   [QSLongPressEffect.State.RUNNING_BACKWARDS_FROM_UP]
      */
-    private fun TestScope.assertEffectReverses() {
-        val action by collectLastValue(longPressEffect.actionType)
-
-        assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.RUNNING_BACKWARDS)
-        assertThat(action).isEqualTo(QSLongPressEffect.ActionType.REVERSE_ANIMATOR)
+    private fun assertEffectReverses(reversingState: QSLongPressEffect.State) {
+        assertThat(longPressEffect.state).isEqualTo(reversingState)
+        verify(callback, times(1)).onReverseAnimator()
     }
 }
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 f375ec7..48caf3e 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
@@ -140,6 +140,27 @@
         }
 
     @Test
+    fun panelAlpha() =
+        testScope.runTest {
+            assertThat(underTest.panelAlpha.value).isEqualTo(1f)
+
+            underTest.setPanelAlpha(0.1f)
+            assertThat(underTest.panelAlpha.value).isEqualTo(0.1f)
+
+            underTest.setPanelAlpha(0.2f)
+            assertThat(underTest.panelAlpha.value).isEqualTo(0.2f)
+
+            underTest.setPanelAlpha(0.3f)
+            assertThat(underTest.panelAlpha.value).isEqualTo(0.3f)
+
+            underTest.setPanelAlpha(0.5f)
+            assertThat(underTest.panelAlpha.value).isEqualTo(0.5f)
+
+            underTest.setPanelAlpha(1.0f)
+            assertThat(underTest.panelAlpha.value).isEqualTo(1f)
+        }
+
+    @Test
     fun topClippingBounds() =
         testScope.runTest {
             assertThat(underTest.topClippingBounds.value).isNull()
@@ -333,27 +354,16 @@
         }
 
     @Test
-    fun isDreamingFromKeyguardUpdateMonitor() =
-        TestScope(mainDispatcher).runTest {
-            whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
-            var latest: Boolean? = null
-            val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
+    fun isDreaming() =
+        testScope.runTest {
+            val isDreaming by collectLastValue(underTest.isDreaming)
+            assertThat(isDreaming).isFalse()
 
-            runCurrent()
-            assertThat(latest).isFalse()
+            underTest.setDreaming(true)
+            assertThat(isDreaming).isTrue()
 
-            val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
-            verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
-            captor.value.onDreamingStateChanged(true)
-            runCurrent()
-            assertThat(latest).isTrue()
-
-            captor.value.onDreamingStateChanged(false)
-            runCurrent()
-            assertThat(latest).isFalse()
-
-            job.cancel()
+            underTest.setDreaming(false)
+            assertThat(isDreaming).isFalse()
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index cfc6b33..d20fec4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -32,10 +32,14 @@
 
 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 androidx.test.filters.SmallTest
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -76,6 +80,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun transitionToGone_keyguardOccluded_biometricAuthenticated() =
         testScope.runTest {
             transitionRepository.sendTransitionSteps(
@@ -96,6 +101,25 @@
         }
 
     @Test
+    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun transitionToGone_keyguardOccludedThenAltBouncer_authed_wmStateRefactor() =
+        testScope.runTest {
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.OCCLUDED,
+                to = KeyguardState.ALTERNATE_BOUNCER,
+                testScope
+            )
+            reset(transitionRepository)
+
+            // Authentication results in calling startDismissKeyguardTransition.
+            kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition()
+            runCurrent()
+
+            assertThat(transitionRepository)
+                .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
+        }
+
+    @Test
     fun noTransition_keyguardNotOccluded_biometricAuthenticated() =
         testScope.runTest {
             transitionRepository.sendTransitionSteps(
@@ -143,4 +167,37 @@
                     to = KeyguardState.OCCLUDED
                 )
         }
+
+    @Test
+    fun transitionToGone_whenOpeningGlanceableHubEditMode() =
+        testScope.runTest {
+            kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true)
+            runCurrent()
+
+            // On Glanceable hub and edit mode activity is started
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.GLANCEABLE_HUB,
+                to = KeyguardState.ALTERNATE_BOUNCER,
+                testScope
+            )
+            reset(transitionRepository)
+
+            kosmos.communalInteractor.setEditModeOpen(true)
+            runCurrent()
+
+            // Auth and alternate bouncer is hidden
+            kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(false)
+            advanceTimeBy(200) // advance past delay
+
+            // Then no transition should occur yet
+            assertThat(transitionRepository).noTransitionsStarted()
+
+            // When keyguard is going away
+            kosmos.fakeKeyguardRepository.setKeyguardGoingAway(true)
+            runCurrent()
+
+            // Then transition to GONE should occur
+            assertThat(transitionRepository)
+                .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 6c5001a..6eb9862 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -53,6 +53,7 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
 import com.android.systemui.testKosmos
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -299,6 +300,7 @@
     fun testTransitionToOccluded_onWake() =
         testScope.runTest {
             kosmos.fakeKeyguardRepository.setKeyguardOccluded(true)
+            kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true)
             powerInteractor.setAwakeForTest()
             advanceTimeBy(100) // account for debouncing
 
@@ -312,6 +314,7 @@
         testScope.runTest {
             kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
             kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
+            kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition()
             powerInteractor.setAwakeForTest()
             advanceTimeBy(100) // account for debouncing
 
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 addbdb6..7906a82 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
@@ -27,6 +27,7 @@
 import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 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
@@ -191,6 +192,7 @@
     fun dismissAlpha() =
         testScope.runTest {
             val dismissAlpha by collectLastValue(underTest.dismissAlpha)
+            assertThat(dismissAlpha).isEqualTo(1f)
 
             keyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.AOD,
@@ -202,9 +204,9 @@
             // User begins to swipe up
             shadeRepository.setLegacyShadeExpansion(0.99f)
 
-            // When not dismissable, no alpha value (null) should emit
+            // When not dismissable, the last alpha value should still be present
             repository.setKeyguardDismissible(false)
-            assertThat(dismissAlpha).isNull()
+            assertThat(dismissAlpha).isEqualTo(1f)
 
             repository.setKeyguardDismissible(true)
             shadeRepository.setLegacyShadeExpansion(0.98f)
@@ -212,9 +214,11 @@
         }
 
     @Test
-    fun dismissAlpha_whenShadeIsExpandedEmitsNull() =
+    fun dismissAlpha_whenShadeResetsEmitsOne() =
         testScope.runTest {
-            val dismissAlpha by collectLastValue(underTest.dismissAlpha)
+            val dismissAlpha by collectValues(underTest.dismissAlpha)
+            assertThat(dismissAlpha[0]).isEqualTo(1f)
+            assertThat(dismissAlpha.size).isEqualTo(1)
 
             keyguardTransitionRepository.sendTransitionSteps(
                 from = KeyguardState.AOD,
@@ -222,14 +226,50 @@
                 testScope,
             )
 
-            repository.setStatusBarState(StatusBarState.SHADE_LOCKED)
-            shadeRepository.setQsExpansion(1f)
+            // User begins to swipe up
+            repository.setStatusBarState(StatusBarState.KEYGUARD)
+            repository.setKeyguardDismissible(true)
+            shadeRepository.setLegacyShadeExpansion(0.98f)
 
-            repository.setKeyguardDismissible(false)
-            assertThat(dismissAlpha).isNull()
+            assertThat(dismissAlpha[1]).isGreaterThan(0.5f)
+            assertThat(dismissAlpha[1]).isLessThan(1f)
+            assertThat(dismissAlpha.size).isEqualTo(2)
+
+            // Now reset the shade
+            shadeRepository.setLegacyShadeExpansion(1f)
+            assertThat(dismissAlpha[2]).isEqualTo(1f)
+            assertThat(dismissAlpha.size).isEqualTo(3)
+        }
+
+    @Test
+    fun dismissAlpha_doesNotEmitWhileTransitioning() =
+        testScope.runTest {
+            val dismissAlpha by collectLastValue(underTest.dismissAlpha)
+            assertThat(dismissAlpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    TransitionStep(
+                        from = KeyguardState.AOD,
+                        to = KeyguardState.GONE,
+                        value = 0f,
+                        transitionState = TransitionState.STARTED,
+                    ),
+                    TransitionStep(
+                        from = KeyguardState.AOD,
+                        to = KeyguardState.GONE,
+                        value = 0.1f,
+                        transitionState = TransitionState.RUNNING,
+                    ),
+                ),
+                testScope,
+            )
 
             repository.setKeyguardDismissible(true)
-            assertThat(dismissAlpha).isNull()
+            shadeRepository.setLegacyShadeExpansion(0.98f)
+
+            // Should still be one
+            assertThat(dismissAlpha).isEqualTo(1f)
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 5756bca..0f061de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -125,68 +125,6 @@
         }
 
     @Test
-    fun dozeAmountTransitionTest_AodToFromLockscreen() =
-        testScope.runTest {
-            val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
-
-            val steps = mutableListOf<TransitionStep>()
-
-            steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
-            steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
-            steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
-            steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
-            steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
-            steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
-            steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
-
-            steps.forEach {
-                repository.sendTransitionStep(it)
-                runCurrent()
-            }
-
-            assertThat(dozeAmountSteps.subList(0, 3))
-                .isEqualTo(
-                    listOf(
-                        steps[0].copy(value = 1f - steps[0].value),
-                        steps[1].copy(value = 1f - steps[1].value),
-                        steps[2].copy(value = 1f - steps[2].value),
-                    )
-                )
-            assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
-        }
-
-    @Test
-    fun dozeAmountTransitionTest_AodToFromGone() =
-        testScope.runTest {
-            val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
-
-            val steps = mutableListOf<TransitionStep>()
-
-            steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
-            steps.add(TransitionStep(AOD, GONE, 0.3f, RUNNING))
-            steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
-            steps.add(TransitionStep(GONE, AOD, 0f, STARTED))
-            steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING))
-            steps.add(TransitionStep(GONE, AOD, 0.3f, RUNNING))
-            steps.add(TransitionStep(GONE, AOD, 1f, FINISHED))
-
-            steps.forEach {
-                repository.sendTransitionStep(it)
-                runCurrent()
-            }
-
-            assertThat(dozeAmountSteps.subList(0, 3))
-                .isEqualTo(
-                    listOf(
-                        steps[0].copy(value = 1f - steps[0].value),
-                        steps[1].copy(value = 1f - steps[1].value),
-                        steps[2].copy(value = 1f - steps[2].value),
-                    )
-                )
-            assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
-        }
-
-    @Test
     fun finishedKeyguardStateTests() =
         testScope.runTest {
             val finishedSteps by collectValues(underTest.finishedKeyguardState)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 9dc930b..6e16705 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -197,7 +197,7 @@
 
     @Test
     @EnableSceneContainer
-    fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() =
+    fun surfaceBehindVisibility_fromLockscreenToGone_noUserInput_trueThroughout() =
         testScope.runTest {
             val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
             val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
@@ -249,6 +249,43 @@
 
     @Test
     @EnableSceneContainer
+    fun surfaceBehindVisibility_fromLockscreenToGone_withUserInput_falseUntilInputStops() =
+        testScope.runTest {
+            val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+            // Before the transition, we start on Lockscreen so the surface should start invisible.
+            kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen))
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(isSurfaceBehindVisible).isFalse()
+
+            // Unlocked with fingerprint.
+            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+
+            // Start the transition to Gone, the surface should not be visible while
+            // isUserInputOngoing is true
+            val isUserInputOngoing = MutableStateFlow(true)
+            kosmos.setSceneTransition(
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Lockscreen,
+                    toScene = Scenes.Gone,
+                    isInitiatedByUserInput = true,
+                    isUserInputOngoing = isUserInputOngoing,
+                    progress = flowOf(0.51f),
+                    currentScene = flowOf(Scenes.Gone),
+                )
+            )
+            assertThat(isSurfaceBehindVisible).isFalse()
+
+            // When isUserInputOngoing becomes false, then the surface should become visible.
+            isUserInputOngoing.value = false
+            assertThat(isSurfaceBehindVisible).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
     fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() =
         testScope.runTest {
             val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
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 519bb6e..63d06a4 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
@@ -55,6 +55,8 @@
 
     @Mock private lateinit var burnInInteractor: BurnInInteractor
     @Mock private lateinit var goneToAodTransitionViewModel: GoneToAodTransitionViewModel
+    @Mock
+    private lateinit var lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel
     @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var clockController: ClockController
 
     private val kosmos = testKosmos()
@@ -76,7 +78,12 @@
         kosmos.burnInInteractor = burnInInteractor
         whenever(goneToAodTransitionViewModel.enterFromTopTranslationY(anyInt()))
             .thenReturn(emptyFlow())
+        whenever(goneToAodTransitionViewModel.enterFromSideTranslationX(anyInt()))
+            .thenReturn(emptyFlow())
+        whenever(lockscreenToAodTransitionViewModel.enterFromSideTranslationX(anyInt()))
+            .thenReturn(emptyFlow())
         kosmos.goneToAodTransitionViewModel = goneToAodTransitionViewModel
+        kosmos.lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel
         kosmos.fakeKeyguardClockRepository.setCurrentClock(clockController)
 
         underTest = kosmos.aodBurnInViewModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
index 460a1fc..b0959e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModelTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -58,6 +59,62 @@
             assertThat(viewModel?.tint).isEqualTo(Color.WHITE)
         }
 
+    @Test
+    fun startsDozing_doNotShowAodVariant() =
+        testScope.runTest {
+            val viewModel by collectLastValue(underTest.viewModel)
+
+            givenUdfpsEnrolledAndEnabled()
+            kosmos.run {
+                fakeKeyguardTransitionRepository.sendTransitionSteps(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.DOZING,
+                    testScope = testScope,
+                    throughTransitionState = TransitionState.STARTED,
+                )
+            }
+
+            assertThat(viewModel?.useAodVariant).isEqualTo(false)
+        }
+
+    @Test
+    fun finishedDozing_showAodVariant() =
+        testScope.runTest {
+            val viewModel by collectLastValue(underTest.viewModel)
+
+            givenUdfpsEnrolledAndEnabled()
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.AOD,
+                testScope = testScope,
+                throughTransitionState = TransitionState.FINISHED,
+            )
+
+            assertThat(viewModel?.useAodVariant).isEqualTo(true)
+        }
+
+    @Test
+    fun startTransitionToLockscreenFromDozing_doNotShowAodVariant() =
+        testScope.runTest {
+            val viewModel by collectLastValue(underTest.viewModel)
+
+            givenUdfpsEnrolledAndEnabled()
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DOZING,
+                testScope = testScope,
+                throughTransitionState = TransitionState.FINISHED,
+            )
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.DOZING,
+                to = KeyguardState.LOCKSCREEN,
+                testScope = testScope,
+                throughTransitionState = TransitionState.RUNNING,
+            )
+
+            assertThat(viewModel?.useAodVariant).isEqualTo(false)
+        }
+
     private fun givenUdfpsEnrolledAndEnabled() {
         kosmos.fakeFingerprintPropertyRepository.supportsUdfps()
         kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 68fbd1c..6d9c271 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -225,13 +225,13 @@
             setUpState(isUdfpsSupported = true)
 
             assertThat(accessibilityDelegateHint)
-                .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE)
+                .isEqualTo(DeviceEntryIconView.AccessibilityHintType.BOUNCER)
 
             // non-interactive lock icon
             fingerprintPropertyRepository.supportsRearFps()
 
             assertThat(accessibilityDelegateHint)
-                .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE)
+                .isEqualTo(DeviceEntryIconView.AccessibilityHintType.BOUNCER)
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
index 79671b8..bf3231e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModelTest.kt
@@ -53,7 +53,7 @@
     @Test
     fun lockscreenAlpha() =
         testScope.runTest {
-            val viewState = ViewStateAccessor(alpha = { 0.6f })
+            val viewState = ViewStateAccessor()
             val alpha by collectValues(underTest.lockscreenAlpha(viewState))
 
             keyguardTransitionRepository.sendTransitionSteps(
@@ -62,11 +62,9 @@
                 testScope
             )
 
-            assertThat(alpha[0]).isEqualTo(0.6f)
-            // Fades out just prior to halfway
+            // Remain at zero throughout
+            assertThat(alpha[0]).isEqualTo(0f)
             assertThat(alpha[1]).isEqualTo(0f)
-            // Must finish at 0
-            assertThat(alpha[2]).isEqualTo(0f)
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
index 716c40d..bab466a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelTest.kt
@@ -28,6 +28,9 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.powerRepository
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
 import com.android.systemui.testKosmos
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
@@ -47,10 +50,18 @@
     private val underTest = kosmos.goneToAodTransitionViewModel
     private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
     private val biometricSettingsRepository = kosmos.biometricSettingsRepository
+    private val powerRepository = kosmos.powerRepository
 
     @Test
-    fun enterFromTopTranslationY() =
+    fun enterFromTopTranslationY_whenNotOnFold() =
         testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.POWER_BUTTON,
+                powerButtonLaunchGestureTriggered = false
+            )
+
             val pixels = -100f
             val enterFromTopTranslationY by
                 collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
@@ -88,6 +99,80 @@
         }
 
     @Test
+    fun enterFromTopTranslationY_whenOnFold_emitsNothing() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.FOLD,
+                powerButtonLaunchGestureTriggered = false
+            )
+
+            val pixels = -100f
+            val enterFromTopTranslationY by
+                collectLastValue(underTest.enterFromTopTranslationY(pixels.toInt()))
+            runCurrent()
+
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(enterFromTopTranslationY).isNull()
+
+            repository.sendTransitionStep(step(.55f))
+            assertThat(enterFromTopTranslationY).isNull()
+
+            repository.sendTransitionStep(step(.85f))
+            assertThat(enterFromTopTranslationY).isNull()
+
+            repository.sendTransitionStep(step(1f))
+            assertThat(enterFromTopTranslationY).isNull()
+        }
+
+    @Test
+    fun enterFromSideTranslationX_onFold() =
+        testScope.runTest {
+            powerRepository.updateWakefulness(
+                rawState = WakefulnessState.STARTING_TO_SLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.FOLD,
+                powerButtonLaunchGestureTriggered = false
+            )
+
+            val pixels = -100f
+            val enterFromSideTranslationX by
+                collectLastValue(underTest.enterFromSideTranslationX(pixels.toInt()))
+            runCurrent()
+
+            // The animation should only start > .4f way through
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(enterFromSideTranslationX)
+                .isEqualTo(
+                    StateToValue(
+                        from = KeyguardState.GONE,
+                        to = KeyguardState.AOD,
+                        transitionState = TransitionState.STARTED,
+                        value = pixels
+                    )
+                )
+
+            repository.sendTransitionStep(step(.55f))
+            assertThat(enterFromSideTranslationX!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
+
+            repository.sendTransitionStep(step(.85f))
+            assertThat(enterFromSideTranslationX!!.value ?: -1f).isIn(Range.closed(pixels, 0f))
+
+            // At the end, the translation should be complete and set to zero
+            repository.sendTransitionStep(step(1f))
+            assertThat(enterFromSideTranslationX)
+                .isEqualTo(
+                    StateToValue(
+                        from = KeyguardState.GONE,
+                        to = KeyguardState.AOD,
+                        transitionState = TransitionState.RUNNING,
+                        value = 0f
+                    )
+                )
+        }
+
+    @Test
     fun enterFromTopAnimationAlpha() =
         testScope.runTest {
             val enterFromTopAnimationAlpha by collectLastValue(underTest.enterFromTopAnimationAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
index 59a6ce7..80a9532 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModelTest.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -32,6 +33,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
+import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
@@ -104,6 +106,24 @@
             values.forEach { assertThat(it).isNull() }
         }
 
+    @Test
+    fun notificationAlpha_fadesOut() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.notificationAlpha)
+
+            keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            assertThat(alpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(0.25f))
+            assertThat(alpha).isIn(Range.open(.25f, .75f))
+
+            keyguardTransitionRepository.sendTransitionStep(step(1f))
+            assertThat(alpha).isEqualTo(0f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(1f, TransitionState.FINISHED))
+            assertThat(alpha).isEqualTo(1f)
+        }
+
     private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
         return TransitionStep(
             from = KeyguardState.GONE,
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 49df345..194f362 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
@@ -39,7 +39,9 @@
 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.scene.data.repository.Idle
 import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shadeTestUtil
@@ -290,6 +292,7 @@
                 testScope,
             )
 
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
             // Make sure the value hasn't changed since we're GONE
             keyguardRepository.topClippingBounds.value = 5
             assertThat(topClippingBounds).isEqualTo(1000)
@@ -518,11 +521,14 @@
                 to = KeyguardState.GONE,
                 testScope = testScope,
             )
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
             assertThat(alpha).isEqualTo(0f)
 
-            // Try pulling down shade and ensure the value doesn't change
-            shadeTestUtil.setQsExpansion(0.5f)
-            assertThat(alpha).isEqualTo(0f)
+            if (!SceneContainerFlag.isEnabled) {
+                // Try pulling down shade and ensure the value doesn't change
+                shadeTestUtil.setQsExpansion(0.5f)
+                assertThat(alpha).isEqualTo(0f)
+            }
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index f46ca00..61d8216 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -50,6 +50,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlin.math.pow
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
 import org.junit.BeforeClass
 import org.junit.Test
@@ -205,8 +206,13 @@
                         pointerCount = if (downWithTwoPointers) 2 else 1,
                     )
                 )
-
-            assertThat(downDestination?.toScene)
+            val downScene by
+                collectLastValue(
+                    downDestination?.let {
+                        kosmos.sceneInteractor.resolveSceneFamily(downDestination.toScene)
+                    } ?: flowOf(null)
+                )
+            assertThat(downScene)
                 .isEqualTo(
                     expectedDownDestination(
                         downFromEdge = downFromEdge,
@@ -223,7 +229,14 @@
                     )
                 )
 
-            assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
+            val upScene by
+                collectLastValue(
+                    destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene?.let { scene ->
+                        kosmos.sceneInteractor.resolveSceneFamily(scene)
+                    } ?: flowOf(null)
+                )
+
+            assertThat(upScene)
                 .isEqualTo(
                     expectedUpDestination(
                         canSwipeToEnter = canSwipeToEnter,
@@ -231,7 +244,14 @@
                     )
                 )
 
-            assertThat(destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene)
+            val leftScene by
+                collectLastValue(
+                    destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene?.let { scene ->
+                        kosmos.sceneInteractor.resolveSceneFamily(scene)
+                    } ?: flowOf(null)
+                )
+
+            assertThat(leftScene)
                 .isEqualTo(
                     expectedLeftDestination(
                         isCommunalAvailable = isCommunalAvailable,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index 7a37a9e..bc0512a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -56,11 +56,15 @@
             underTest.addSelectedUserMediaEntry(userMedia)
 
             assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+            assertThat(underTest.hasActiveMedia()).isTrue()
+            assertThat(underTest.hasAnyMedia()).isTrue()
 
             underTest.addSelectedUserMediaEntry(userMedia.copy(active = false))
 
             assertThat(selectedUserEntries?.get(instanceId)).isNotEqualTo(userMedia)
             assertThat(selectedUserEntries?.get(instanceId)?.active).isFalse()
+            assertThat(underTest.hasActiveMedia()).isFalse()
+            assertThat(underTest.hasAnyMedia()).isTrue()
         }
 
     @Test
@@ -74,8 +78,12 @@
             underTest.addSelectedUserMediaEntry(userMedia)
 
             assertThat(selectedUserEntries?.get(instanceId)).isEqualTo(userMedia)
+            assertThat(underTest.hasActiveMedia()).isTrue()
+            assertThat(underTest.hasAnyMedia()).isTrue()
 
             assertThat(underTest.removeSelectedUserMediaEntry(instanceId, userMedia)).isTrue()
+            assertThat(underTest.hasActiveMedia()).isFalse()
+            assertThat(underTest.hasAnyMedia()).isFalse()
         }
 
     @Test
@@ -144,7 +152,7 @@
             underTest.setRecommendation(mediaRecommendation.copy(isActive = false))
 
             assertThat(smartspaceMediaData).isNotEqualTo(mediaRecommendation)
-            assertThat(smartspaceMediaData?.isActive).isFalse()
+            assertThat(underTest.isRecommendationActive()).isFalse()
         }
 
     @Test
@@ -349,6 +357,14 @@
                 .inOrder()
         }
 
+    @Test
+    fun hasAnyMedia_noMediaSet_returnsFalse() =
+        testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() }
+
+    @Test
+    fun hasActiveMedia_noMediaSet_returnsFalse() =
+        testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() }
+
     private fun createMediaData(
         app: String,
         playing: Boolean,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
index 39dbc7e..c62195f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaCarouselInteractorTest.kt
@@ -76,22 +76,20 @@
         testScope.runTest {
             val hasActiveMediaOrRecommendation by
                 collectLastValue(underTest.hasActiveMediaOrRecommendation)
-            val hasActiveMedia by collectLastValue(underTest.hasActiveMedia)
-            val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
 
             val userMedia = MediaData(active = true)
 
             mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
 
             assertThat(hasActiveMediaOrRecommendation).isTrue()
-            assertThat(hasActiveMedia).isTrue()
-            assertThat(hasAnyMedia).isTrue()
+            assertThat(underTest.hasActiveMedia()).isTrue()
+            assertThat(underTest.hasAnyMedia()).isTrue()
 
             mediaFilterRepository.addSelectedUserMediaEntry(userMedia.copy(active = false))
 
             assertThat(hasActiveMediaOrRecommendation).isFalse()
-            assertThat(hasActiveMedia).isFalse()
-            assertThat(hasAnyMedia).isTrue()
+            assertThat(underTest.hasActiveMedia()).isFalse()
+            assertThat(underTest.hasAnyMedia()).isTrue()
         }
 
     @Test
@@ -99,8 +97,6 @@
         testScope.runTest {
             val hasActiveMediaOrRecommendation by
                 collectLastValue(underTest.hasActiveMediaOrRecommendation)
-            val hasActiveMedia by collectLastValue(underTest.hasActiveMedia)
-            val hasAnyMedia by collectLastValue(underTest.hasAnyMedia)
 
             val userMedia = MediaData(active = false)
             val instanceId = userMedia.instanceId
@@ -109,8 +105,8 @@
             mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
 
             assertThat(hasActiveMediaOrRecommendation).isFalse()
-            assertThat(hasActiveMedia).isFalse()
-            assertThat(hasAnyMedia).isTrue()
+            assertThat(underTest.hasActiveMedia()).isFalse()
+            assertThat(underTest.hasAnyMedia()).isTrue()
 
             assertThat(mediaFilterRepository.removeSelectedUserMediaEntry(instanceId, userMedia))
                 .isTrue()
@@ -119,8 +115,8 @@
             )
 
             assertThat(hasActiveMediaOrRecommendation).isFalse()
-            assertThat(hasActiveMedia).isFalse()
-            assertThat(hasAnyMedia).isFalse()
+            assertThat(underTest.hasActiveMedia()).isFalse()
+            assertThat(underTest.hasAnyMedia()).isFalse()
         }
 
     @Test
@@ -147,6 +143,7 @@
 
             mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
             mediaFilterRepository.addMediaDataLoadingState(mediaLoadingModel)
+            mediaFilterRepository.setOrderedMedia()
 
             assertThat(hasActiveMediaOrRecommendation).isTrue()
             assertThat(hasAnyMediaOrRecommendation).isTrue()
@@ -202,7 +199,7 @@
 
     @Test
     fun hasAnyMedia_noMediaSet_returnsFalse() =
-        testScope.runTest { assertThat(underTest.hasAnyMedia.value).isFalse() }
+        testScope.runTest { assertThat(underTest.hasAnyMedia()).isFalse() }
 
     @Test
     fun hasAnyMediaOrRecommendation_noMediaSet_returnsFalse() =
@@ -210,7 +207,7 @@
 
     @Test
     fun hasActiveMedia_noMediaSet_returnsFalse() =
-        testScope.runTest { assertThat(underTest.hasActiveMedia.value).isFalse() }
+        testScope.runTest { assertThat(underTest.hasActiveMedia()).isFalse() }
 
     @Test
     fun hasActiveMediaOrRecommendation_nothingSet_returnsFalse() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/data/repository/NavigationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/data/repository/NavigationRepositoryTest.kt
new file mode 100644
index 0000000..e45aa05
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/data/repository/NavigationRepositoryTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.navigation.data.repository
+
+import android.view.WindowManagerPolicyConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
+import com.android.systemui.navigationbar.navigationModeController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavigationRepositoryTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val navigationModeControllerMock = kosmos.navigationModeController
+
+    private val underTest = kosmos.navigationRepository
+
+    private var currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+    private val modeChangedListeners = mutableListOf<ModeChangedListener>()
+
+    @Before
+    fun setUp() {
+        whenever(navigationModeControllerMock.addListener(any())).thenAnswer { invocation ->
+            val listener = invocation.arguments[0] as ModeChangedListener
+            modeChangedListeners.add(listener)
+            currentMode
+        }
+    }
+
+    @Test
+    fun isGesturalMode() =
+        testScope.runTest {
+            val isGesturalMode by collectLastValue(underTest.isGesturalMode)
+            assertThat(isGesturalMode).isFalse()
+
+            currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+            notifyModeChangedListeners()
+            assertThat(isGesturalMode).isTrue()
+
+            currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+            notifyModeChangedListeners()
+            assertThat(isGesturalMode).isFalse()
+        }
+
+    private fun notifyModeChangedListeners() {
+        modeChangedListeners.forEach { listener -> listener.onNavigationModeChanged(currentMode) }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorTest.kt
new file mode 100644
index 0000000..88beeb2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.navigation.domain.interactor
+
+import android.view.WindowManagerPolicyConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
+import com.android.systemui.navigationbar.navigationModeController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NavigationInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val navigationModeControllerMock = kosmos.navigationModeController
+
+    private val underTest = kosmos.navigationInteractor
+
+    private var currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+    private val modeChangedListeners = mutableListOf<ModeChangedListener>()
+
+    @Before
+    fun setUp() {
+        whenever(navigationModeControllerMock.addListener(any())).thenAnswer { invocation ->
+            val listener = invocation.arguments[0] as ModeChangedListener
+            modeChangedListeners.add(listener)
+            currentMode
+        }
+    }
+
+    @Test
+    fun isGesturalMode() =
+        testScope.runTest {
+            val isGesturalMode by collectLastValue(underTest.isGesturalMode)
+            assertThat(isGesturalMode).isFalse()
+
+            currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
+            notifyModeChangedListeners()
+            assertThat(isGesturalMode).isTrue()
+
+            currentMode = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
+            notifyModeChangedListeners()
+            assertThat(isGesturalMode).isFalse()
+        }
+
+    private fun notifyModeChangedListeners() {
+        modeChangedListeners.forEach { listener -> listener.onNavigationModeChanged(currentMode) }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
index 9d8ec95..cb4e2d3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
@@ -31,6 +31,8 @@
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneViewModel
 import com.android.systemui.testKosmos
@@ -62,7 +64,9 @@
             val destinationScenes by collectLastValue(underTest.destinationScenes)
             lockDevice()
 
-            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
+                .isEqualTo(Scenes.Lockscreen)
         }
 
     @Test
@@ -72,7 +76,8 @@
             lockDevice()
             unlockDevice()
 
-            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Gone)
+            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+            assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
         }
 
     @Test
@@ -85,7 +90,9 @@
             )
             sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
 
-            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
+                .isEqualTo(Scenes.Lockscreen)
         }
 
     @Test
@@ -96,10 +103,12 @@
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.None
             )
+            sceneInteractor // force the lazy; this will kick off StateFlows
             runCurrent()
             sceneInteractor.changeScene(Scenes.Gone, "reason")
 
-            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Gone)
+            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
         }
 
     private fun TestScope.lockDevice() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt
new file mode 100644
index 0000000..9d2528d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.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.qs.panels.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC
+import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC
+import com.android.systemui.statusbar.policy.PolicyModule.Companion.WORK_MODE_TILE_SPEC
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NewTilesAvailabilityInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos().apply {
+        tileAvailabilityInteractorsMap = buildMap {
+            put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor)
+            put(WORK_MODE_TILE_SPEC, FakeTileAvailabilityInteractor(
+                    mapOf(
+                       fakeUserRepository.getSelectedUserInfo().id to flowOf(true),
+                    ).withDefault { flowOf(false) }
+            ))
+            put(HOTSPOT_TILE_SPEC, FakeTileAvailabilityInteractor(
+                    emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+            ))
+        }
+    }
+
+    private val underTest by lazy { kosmos.newTilesAvailabilityInteractor }
+
+    @Test
+    fun defaultUser_getAvailabilityFlow() = with(kosmos) {
+        testScope.runTest {
+            val availability by collectLastValue(underTest.newTilesAvailable)
+
+            assertThat(availability).isEqualTo(
+                    mapOf(
+                            TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true,
+                            TileSpec.create(WORK_MODE_TILE_SPEC) to true,
+                            TileSpec.create(HOTSPOT_TILE_SPEC) to false,
+                    )
+            )
+        }
+    }
+
+    @Test
+    fun getAvailabilityFlow_userChange() = with(kosmos) {
+        testScope.runTest {
+            val availability by collectLastValue(underTest.newTilesAvailable)
+            fakeUserRepository.asMainUser()
+
+            assertThat(availability).isEqualTo(
+                    mapOf(
+                            TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true,
+                            TileSpec.create(WORK_MODE_TILE_SPEC) to false,
+                            TileSpec.create(HOTSPOT_TILE_SPEC) to false,
+                    )
+            )
+        }
+    }
+
+    @Test
+    fun noAvailabilityInteractor_emptyMap() = with(kosmos) {
+        testScope.runTest {
+            tileAvailabilityInteractorsMap = emptyMap()
+
+            val availability by collectLastValue(underTest.newTilesAvailable)
+
+            assertThat(availability).isEmpty()
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt
new file mode 100644
index 0000000..5a58597
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt
@@ -0,0 +1,228 @@
+/*
+ * 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.domain.interactor
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_QS_NEW_TILES
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.qs.QSFactory
+import com.android.systemui.qs.FakeQSFactory
+import com.android.systemui.qs.FakeQSTile
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC
+import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC
+import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.INTERNET_TILE_SPEC
+import com.android.systemui.statusbar.policy.PolicyModule.Companion.FLASHLIGHT_TILE_SPEC
+import com.android.systemui.statusbar.policy.PolicyModule.Companion.WORK_MODE_TILE_SPEC
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.data.repository.userRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+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
+
+@RunWith(ParameterizedAndroidJunit4::class)
+@SmallTest
+class TilesAvailabilityInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    private val createdTiles = mutableListOf<FakeQSTile>()
+
+    private val kosmos = testKosmos().apply {
+        tileAvailabilityInteractorsMap = buildMap {
+            put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor)
+            put(WORK_MODE_TILE_SPEC, FakeTileAvailabilityInteractor(
+                    mapOf(
+                            fakeUserRepository.getSelectedUserInfo().id to flowOf(true),
+                    ).withDefault { flowOf(false) }
+            ))
+            put(HOTSPOT_TILE_SPEC, FakeTileAvailabilityInteractor(
+                    emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+            ))
+        }
+
+        qsTileFactory = constantFactory(
+                tilesForCreator(
+                        userRepository.getSelectedUserInfo().id,
+                        mapOf(
+                                AIRPLANE_MODE_TILE_SPEC to false,
+                                WORK_MODE_TILE_SPEC to false,
+                                HOTSPOT_TILE_SPEC to true,
+                                INTERNET_TILE_SPEC to true,
+                                FLASHLIGHT_TILE_SPEC to false,
+                        )
+                )
+        )
+    }
+
+    private val underTest by lazy { kosmos.tilesAvailabilityInteractor }
+
+    @Test
+    @DisableFlags(FLAG_QS_NEW_TILES)
+    fun flagOff_usesAvailabilityFromFactoryTiles() = with(kosmos) {
+        testScope.runTest {
+            val unavailableTiles = underTest.getUnavailableTiles(
+                    setOf(
+                            AIRPLANE_MODE_TILE_SPEC,
+                            WORK_MODE_TILE_SPEC,
+                            HOTSPOT_TILE_SPEC,
+                            INTERNET_TILE_SPEC,
+                            FLASHLIGHT_TILE_SPEC,
+                    ).map(TileSpec::create)
+            )
+            assertThat(unavailableTiles).isEqualTo(setOf(
+                    AIRPLANE_MODE_TILE_SPEC,
+                    WORK_MODE_TILE_SPEC,
+                    FLASHLIGHT_TILE_SPEC,
+            ).mapTo(mutableSetOf(), TileSpec::create))
+        }
+    }
+
+    @Test
+    fun tileCannotBeCreated_isUnavailable() = with(kosmos) {
+        testScope.runTest {
+            val badSpec = TileSpec.create("unknown")
+            val unavailableTiles = underTest.getUnavailableTiles(
+                    setOf(
+                        badSpec
+                    )
+            )
+            assertThat(unavailableTiles).contains(badSpec)
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_QS_NEW_TILES)
+    fun flagOn_defaultsToInteractorTiles_usesFactoryForOthers() = with(kosmos) {
+        testScope.runTest {
+            val unavailableTiles = underTest.getUnavailableTiles(
+                    setOf(
+                            AIRPLANE_MODE_TILE_SPEC,
+                            WORK_MODE_TILE_SPEC,
+                            HOTSPOT_TILE_SPEC,
+                            INTERNET_TILE_SPEC,
+                            FLASHLIGHT_TILE_SPEC,
+                    ).map(TileSpec::create)
+            )
+            assertThat(unavailableTiles).isEqualTo(setOf(
+                    HOTSPOT_TILE_SPEC,
+                    FLASHLIGHT_TILE_SPEC,
+            ).mapTo(mutableSetOf(), TileSpec::create))
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_QS_NEW_TILES)
+    fun flagOn_defaultsToInteractorTiles_usesFactoryForOthers_userChange() = with(kosmos) {
+        testScope.runTest {
+            fakeUserRepository.asMainUser()
+            val unavailableTiles = underTest.getUnavailableTiles(
+                    setOf(
+                            AIRPLANE_MODE_TILE_SPEC,
+                            WORK_MODE_TILE_SPEC,
+                            HOTSPOT_TILE_SPEC,
+                            INTERNET_TILE_SPEC,
+                            FLASHLIGHT_TILE_SPEC,
+                    ).map(TileSpec::create)
+            )
+            assertThat(unavailableTiles).isEqualTo(setOf(
+                    WORK_MODE_TILE_SPEC,
+                    HOTSPOT_TILE_SPEC,
+                    FLASHLIGHT_TILE_SPEC,
+            ).mapTo(mutableSetOf(), TileSpec::create))
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_QS_NEW_TILES)
+    fun flagOn_onlyNeededTilesAreCreated_andThenDestroyed() = with(kosmos) {
+        testScope.runTest {
+            underTest.getUnavailableTiles(
+                    setOf(
+                            AIRPLANE_MODE_TILE_SPEC,
+                            WORK_MODE_TILE_SPEC,
+                            HOTSPOT_TILE_SPEC,
+                            INTERNET_TILE_SPEC,
+                            FLASHLIGHT_TILE_SPEC,
+                    ).map(TileSpec::create)
+            )
+            assertThat(createdTiles.map { it.tileSpec })
+                    .containsExactly(INTERNET_TILE_SPEC, FLASHLIGHT_TILE_SPEC)
+            assertThat(createdTiles.all { it.destroyed }).isTrue()
+        }
+    }
+
+    @Test
+    @DisableFlags(FLAG_QS_NEW_TILES)
+    fun flagOn_TilesAreCreatedAndThenDestroyed() = with(kosmos) {
+        testScope.runTest {
+            val allTiles = setOf(
+                    AIRPLANE_MODE_TILE_SPEC,
+                    WORK_MODE_TILE_SPEC,
+                    HOTSPOT_TILE_SPEC,
+                    INTERNET_TILE_SPEC,
+                    FLASHLIGHT_TILE_SPEC,
+                )
+            underTest.getUnavailableTiles(allTiles.map(TileSpec::create))
+            assertThat(createdTiles.map { it.tileSpec })
+                    .containsExactlyElementsIn(allTiles)
+            assertThat(createdTiles.all { it.destroyed }).isTrue()
+        }
+    }
+
+
+    private fun constantFactory(creatorTiles: Set<FakeQSTile>): QSFactory {
+        return FakeQSFactory { spec ->
+            creatorTiles.firstOrNull { it.tileSpec == spec }?.also {
+                createdTiles.add(it)
+            }
+        }
+    }
+
+    companion object {
+        private fun tilesForCreator(
+                user: Int,
+                specAvailabilities: Map<String, Boolean>
+        ): Set<FakeQSTile> {
+            return specAvailabilities.mapTo(mutableSetOf()) {
+                FakeQSTile(user, it.value).apply {
+                    tileSpec = it.key
+                }
+            }
+        }
+
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(FLAG_QS_NEW_TILES)
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
index 9fb25a28..8b49d43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
@@ -19,30 +19,27 @@
 import android.R
 import android.content.ComponentName
 import android.graphics.drawable.TestStubDrawable
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
 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.common.shared.model.Text
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.FakeQSFactory
 import com.android.systemui.qs.FakeQSTile
 import com.android.systemui.qs.panels.data.repository.stockTilesRepository
-import com.android.systemui.qs.panels.domain.interactor.editTilesListInteractor
-import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap
-import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor
-import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
+import com.android.systemui.qs.panels.domain.interactor.FakeTileAvailabilityInteractor
+import com.android.systemui.qs.panels.domain.interactor.tileAvailabilityInteractorsMap
 import com.android.systemui.qs.panels.shared.model.EditTileData
 import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository
 import com.android.systemui.qs.pipeline.data.repository.MinimumTilesFixedRepository
 import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository
 import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository
 import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
-import com.android.systemui.qs.pipeline.domain.interactor.minimumTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
@@ -57,14 +54,23 @@
 import com.android.systemui.settings.userTracker
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @SmallTest
-class EditModeViewModelTest : SysuiTestCase() {
+class EditModeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
     private val kosmos = testKosmos()
 
     // Only have some configurations so we can test the effect of missing configurations.
@@ -98,17 +104,7 @@
         )
 
     private val underTest: EditModeViewModel by lazy {
-        with(kosmos) {
-            EditModeViewModel(
-                editTilesListInteractor,
-                currentTilesInteractor,
-                minimumTilesInteractor,
-                infiniteGridLayout,
-                applicationCoroutineScope,
-                gridLayoutTypeInteractor,
-                gridLayoutMap,
-            )
-        }
+        kosmos.editModeViewModel
     }
 
     @Before
@@ -464,18 +460,45 @@
             }
         }
 
-    private companion object {
-        val drawable1 = TestStubDrawable("drawable1")
-        val appName1 = "App1"
-        val tileService1 = "Tile Service 1"
-        val component1 = ComponentName("pkg1", "srv1")
+    @Test
+    fun tileNotAvailable_notShowing() = with(kosmos) {
+        testScope.runTest {
+            val unavailableTile = "work"
+            qsTileFactory = FakeQSFactory { spec ->
+                FakeQSTile(userTracker.userId, spec != unavailableTile)
+            }
+            tileAvailabilityInteractorsMap = mapOf(
+                    unavailableTile to FakeTileAvailabilityInteractor(
+                            emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+                    )
+            )
+            val tiles by collectLastValue(underTest.tiles)
+            val currentTiles =
+                    mutableListOf(
+                            TileSpec.create("flashlight"),
+                            TileSpec.create("airplane"),
+                            TileSpec.create("alarm"),
+                    )
+            currentTilesInteractor.setTiles(currentTiles)
 
-        val drawable2 = TestStubDrawable("drawable2")
-        val appName2 = "App2"
-        val tileService2 = "Tile Service 2"
-        val component2 = ComponentName("pkg2", "srv2")
+            underTest.startEditing()
 
-        fun TileSpec.missingConfigEditTileData(): EditTileData {
+            assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) }).isTrue()
+        }
+    }
+
+    companion object {
+        private val drawable1 = TestStubDrawable("drawable1")
+        private val appName1 = "App1"
+        private val tileService1 = "Tile Service 1"
+        private val component1 = ComponentName("pkg1", "srv1")
+
+        private val drawable2 = TestStubDrawable("drawable2")
+        private val appName2 = "App2"
+        private val tileService2 = "Tile Service 2"
+        private val component2 = ComponentName("pkg2", "srv2")
+
+        private fun TileSpec.missingConfigEditTileData(): EditTileData {
             return EditTileData(
                 tileSpec = this,
                 icon = Icon.Resource(R.drawable.star_on, ContentDescription.Loaded(spec)),
@@ -484,7 +507,7 @@
             )
         }
 
-        fun QSTileConfig.toEditTileData(): EditTileData {
+        private fun QSTileConfig.toEditTileData(): EditTileData {
             return EditTileData(
                 tileSpec = tileSpec,
                 icon =
@@ -494,7 +517,7 @@
             )
         }
 
-        fun Kosmos.getEditTileData(tileSpec: TileSpec): EditTileData {
+        private fun Kosmos.getEditTileData(tileSpec: TileSpec): EditTileData {
             return if (qSTileConfigProvider.hasConfig(tileSpec.spec)) {
                 qSTileConfigProvider.getConfig(tileSpec.spec).toEditTileData()
             } else {
@@ -502,6 +525,12 @@
             }
         }
 
-        val minNumberOfTiles = 3
+        private val minNumberOfTiles = 3
+
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(Flags.FLAG_QS_NEW_TILES)
+        }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
index 9bb591e..8ad647d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddableTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.CastTile
 import com.android.systemui.statusbar.policy.CastController
+import com.android.systemui.statusbar.policy.CastDevice
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -73,9 +74,12 @@
     @Test
     fun onCastDevicesChanged_deviceNotConnectedOrConnecting_noSignal() = runTest {
         val device =
-            CastController.CastDevice().apply {
-                state = CastController.CastDevice.STATE_DISCONNECTED
-            }
+            CastDevice(
+                id = "id",
+                name = null,
+                state = CastDevice.CastState.Disconnected,
+                origin = CastDevice.CastOrigin.MediaProjection,
+            )
         whenever(castController.castDevices).thenReturn(listOf(device))
 
         val signal by collectLastValue(underTest.autoAddSignal(0))
@@ -91,11 +95,19 @@
     @Test
     fun onCastDevicesChanged_someDeviceConnecting_addSignal() = runTest {
         val disconnectedDevice =
-            CastController.CastDevice().apply {
-                state = CastController.CastDevice.STATE_DISCONNECTED
-            }
+            CastDevice(
+                id = "id",
+                name = null,
+                state = CastDevice.CastState.Disconnected,
+                origin = CastDevice.CastOrigin.MediaProjection,
+            )
         val connectingDevice =
-            CastController.CastDevice().apply { state = CastController.CastDevice.STATE_CONNECTING }
+            CastDevice(
+                id = "id",
+                name = null,
+                state = CastDevice.CastState.Connecting,
+                origin = CastDevice.CastOrigin.MediaProjection,
+            )
         whenever(castController.castDevices)
             .thenReturn(listOf(disconnectedDevice, connectingDevice))
 
@@ -112,11 +124,19 @@
     @Test
     fun onCastDevicesChanged_someDeviceConnected_addSignal() = runTest {
         val disconnectedDevice =
-            CastController.CastDevice().apply {
-                state = CastController.CastDevice.STATE_DISCONNECTED
-            }
+            CastDevice(
+                id = "id",
+                name = null,
+                state = CastDevice.CastState.Disconnected,
+                origin = CastDevice.CastOrigin.MediaProjection,
+            )
         val connectedDevice =
-            CastController.CastDevice().apply { state = CastController.CastDevice.STATE_CONNECTED }
+            CastDevice(
+                id = "id",
+                name = null,
+                state = CastDevice.CastState.Connected,
+                origin = CastDevice.CastOrigin.MediaProjection,
+            )
         whenever(castController.castDevices).thenReturn(listOf(disconnectedDevice, connectedDevice))
 
         val signal by collectLastValue(underTest.autoAddSignal(0))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
index b7e08da..ff40e43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
@@ -54,7 +54,7 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        whenever(logBufferFactory.create(any(), any(), any())).thenReturn(logBuffer)
+        whenever(logBufferFactory.create(any(), any(), any(), any())).thenReturn(logBuffer)
         val tileSpec: TileSpec = TileSpec.create("chatty_tile")
         underTest =
             QSTileLogger(mapOf(tileSpec to chattyLogBuffer), logBufferFactory, statusBarController)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index e87c8ad..899122d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.screenrecord.RecordingController
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -72,11 +73,18 @@
                 .thenReturn(dialog)
         }
 
+    private val screenRecordRepository =
+        ScreenRecordRepositoryImpl(
+            bgCoroutineContext = testScope.testScheduler,
+            recordingController = recordingController,
+        )
+
     private val underTest =
         ScreenRecordTileUserActionInteractor(
             context,
             testScope.testScheduler,
             testScope.testScheduler,
+            screenRecordRepository,
             recordingController,
             keyguardInteractor,
             keyguardDismissUtil,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index 7388d51..b35b7bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -164,25 +164,16 @@
 
             with(qsImpl!!) {
                 verify(this).setQsVisible(false)
-                verify(this, never())
+                verify(this)
                     .setQsExpansion(
-                        /* expansion= */ anyFloat(),
-                        /* panelExpansionFraction= */ anyFloat(),
-                        /* proposedTranslation= */ anyFloat(),
-                        /* squishinessFraction= */ anyFloat(),
+                        /* expansion= */ 0f,
+                        /* panelExpansionFraction= */ 1f,
+                        /* proposedTranslation= */ 0f,
+                        /* squishinessFraction= */ 1f,
                     )
                 verify(this).setListening(false)
                 verify(this).setExpanded(false)
             }
-
-            underTest.applyLatestExpansionAndSquishiness()
-            verify(qsImpl!!)
-                .setQsExpansion(
-                    /* expansion= */ 0f,
-                    /* panelExpansionFraction= */ 1f,
-                    /* proposedTranslation= */ 0f,
-                    /* squishinessFraction= */ 1f,
-                )
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 0b55bef..5b6fea5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -36,7 +35,6 @@
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.data.repository.mediaFilterRepository
-import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
 import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.qs.FooterActionsController
@@ -46,6 +44,8 @@
 import com.android.systemui.scene.domain.interactor.sceneBackInteractor
 import com.android.systemui.scene.domain.interactor.sceneContainerStartable
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
 import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModel
@@ -55,7 +55,6 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -82,11 +81,8 @@
     private val sceneBackInteractor = kosmos.sceneBackInteractor
     private val sceneContainerStartable = kosmos.sceneContainerStartable
 
-    private val mediaDataManager = mock<MediaDataManager>()
-
     private lateinit var underTest: QuickSettingsSceneViewModel
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Before
     fun setUp() {
         kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false)
@@ -95,7 +91,6 @@
         underTest =
             QuickSettingsSceneViewModel(
                 applicationScope = testScope.backgroundScope,
-                deviceEntryInteractor = kosmos.deviceEntryInteractor,
                 brightnessMirrorViewModel = kosmos.brightnessMirrorViewModel,
                 shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
                 qsSceneAdapter = qsFlexiglassAdapter,
@@ -112,6 +107,7 @@
         testScope.runTest {
             overrideResource(R.bool.config_use_split_notification_shade, false)
             val destinations by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             qsFlexiglassAdapter.setCustomizing(false)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Pin
@@ -128,9 +124,10 @@
                         Swipe(
                             fromSource = Edge.Bottom,
                             direction = SwipeDirection.Up,
-                        ) to UserActionResult(Scenes.Gone)
+                        ) to UserActionResult(SceneFamilies.Home)
                     )
                 )
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
         }
 
     @Test
@@ -142,6 +139,7 @@
 
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             val backScene by collectLastValue(sceneBackInteractor.backScene)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
             sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
             assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
@@ -155,9 +153,10 @@
                         Swipe(
                             fromSource = Edge.Bottom,
                             direction = SwipeDirection.Up,
-                        ) to UserActionResult(Scenes.Lockscreen)
+                        ) to UserActionResult(SceneFamilies.Home)
                     )
                 )
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
         }
 
     @Test
@@ -165,6 +164,7 @@
         testScope.runTest {
             overrideResource(R.bool.config_use_split_notification_shade, false)
             val destinations by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             qsFlexiglassAdapter.setCustomizing(false)
             kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
@@ -179,9 +179,10 @@
                         Swipe(
                             fromSource = Edge.Bottom,
                             direction = SwipeDirection.Up,
-                        ) to UserActionResult(Scenes.Lockscreen)
+                        ) to UserActionResult(SceneFamilies.Home)
                     )
                 )
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
         }
 
     @Test
@@ -199,6 +200,7 @@
         testScope.runTest {
             overrideResource(R.bool.config_use_split_notification_shade, true)
             val destinations by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             qsFlexiglassAdapter.setCustomizing(false)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Pin
@@ -215,9 +217,10 @@
                         Swipe(
                             fromSource = Edge.Bottom,
                             direction = SwipeDirection.Up,
-                        ) to UserActionResult(Scenes.Gone),
+                        ) to UserActionResult(SceneFamilies.Home)
                     )
                 )
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
index 034c2e9..ac67ac8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModelTest.kt
@@ -31,6 +31,8 @@
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.ui.viewmodel.quickSettingsShadeSceneViewModel
 import com.android.systemui.testKosmos
@@ -60,38 +62,45 @@
     fun upTransitionSceneKey_deviceLocked_lockscreen() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             lockDevice()
 
-            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
         }
 
     @Test
     fun upTransitionSceneKey_deviceUnlocked_gone() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             lockDevice()
             unlockDevice()
 
-            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Gone)
+            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
         }
 
     @Test
     fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.None
             )
             sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
 
-            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
         }
 
     @Test
     fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.None
@@ -99,7 +108,8 @@
             runCurrent()
             sceneInteractor.changeScene(Scenes.Gone, "reason")
 
-            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(Scenes.Gone)
+            assertThat(destinationScenes?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
         }
 
     private fun TestScope.lockDevice() {
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 9e7e766..4d5d22c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -51,7 +51,6 @@
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
 import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -61,6 +60,8 @@
 import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
 import com.android.systemui.scene.domain.interactor.sceneContainerStartable
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
@@ -91,7 +92,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -169,8 +169,6 @@
 
     private val qsFlexiglassAdapter = FakeQSSceneAdapter(inflateDelegate = { mock() })
 
-    @Mock private lateinit var mediaDataManager: MediaDataManager
-
     private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
     private lateinit var telecomManager: TelecomManager
     private val fakeSceneDataSource = kosmos.fakeSceneDataSource
@@ -205,7 +203,6 @@
         shadeSceneViewModel =
             ShadeSceneViewModel(
                 applicationScope = testScope.backgroundScope,
-                deviceEntryInteractor = deviceEntryInteractor,
                 shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
                 qsSceneAdapter = qsFlexiglassAdapter,
                 notifications = kosmos.notificationsPlaceholderViewModel,
@@ -280,6 +277,7 @@
     fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
         testScope.runTest {
             val destinationScenes by collectLastValue(shadeSceneViewModel.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
             assertCurrentScene(Scenes.Lockscreen)
 
@@ -288,9 +286,10 @@
             assertCurrentScene(Scenes.Shade)
 
             val upDestinationSceneKey = destinationScenes?.get(Swipe.Up)?.toScene
-            assertThat(upDestinationSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
             emulateUserDrivenTransition(
-                to = upDestinationSceneKey,
+                to = homeScene,
             )
         }
 
@@ -299,6 +298,7 @@
         testScope.runTest {
             val destinationScenes by collectLastValue(shadeSceneViewModel.destinationScenes)
             val canSwipeToEnter by collectLastValue(deviceEntryInteractor.canSwipeToEnter)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
 
             setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
 
@@ -314,9 +314,10 @@
             assertCurrentScene(Scenes.Shade)
 
             val upDestinationSceneKey = destinationScenes?.get(Swipe.Up)?.toScene
-            assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
+            assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
             emulateUserDrivenTransition(
-                to = upDestinationSceneKey,
+                to = homeScene,
             )
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 229a711..ec7150b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -21,23 +21,30 @@
 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.SceneKey
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.Transition
 import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
 import com.android.systemui.scene.sceneContainerConfig
 import com.android.systemui.scene.sceneKeys
-import com.android.systemui.scene.shared.model.SceneContainerConfig
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toList
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -52,48 +59,27 @@
     private val testScope = kosmos.testScope
     private val fakeSceneDataSource = kosmos.fakeSceneDataSource
 
-    private lateinit var underTest: SceneInteractor
+    private val underTest = kosmos.sceneInteractor
 
     @Test
     fun allSceneKeys() {
-        underTest = kosmos.sceneInteractor
         assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys)
     }
 
     @Test
     fun changeScene_toUnknownScene_doesNothing() =
         testScope.runTest {
-            val sceneKeys =
-                listOf(
-                    Scenes.QuickSettings,
-                    Scenes.Shade,
-                    Scenes.Lockscreen,
-                    Scenes.Gone,
-                    Scenes.Communal,
-                )
-            val navigationDistances =
-                mapOf(
-                    Scenes.Gone to 0,
-                    Scenes.Lockscreen to 0,
-                    Scenes.Communal to 1,
-                    Scenes.Shade to 2,
-                    Scenes.QuickSettings to 3,
-                )
-            kosmos.sceneContainerConfig =
-                SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances)
-            underTest = kosmos.sceneInteractor
             val currentScene by collectLastValue(underTest.currentScene)
+            val unknownScene = SceneKey("UNKNOWN")
             val previousScene = currentScene
-            assertThat(previousScene).isNotEqualTo(Scenes.Bouncer)
-            underTest.changeScene(Scenes.Bouncer, "reason")
+            assertThat(previousScene).isNotEqualTo(unknownScene)
+            underTest.changeScene(unknownScene, "reason")
             assertThat(currentScene).isEqualTo(previousScene)
         }
 
     @Test
     fun changeScene() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
-
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -104,8 +90,6 @@
     @Test
     fun changeScene_toGoneWhenUnl_doesNotThrow() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
-
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -120,15 +104,11 @@
 
     @Test(expected = IllegalStateException::class)
     fun changeScene_toGoneWhenStillLocked_throws() =
-        testScope.runTest {
-            underTest = kosmos.sceneInteractor
-            underTest.changeScene(Scenes.Gone, "reason")
-        }
+        testScope.runTest { underTest.changeScene(Scenes.Gone, "reason") }
 
     @Test
     fun changeScene_toGoneWhenTransitionToLockedFromGone() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             val currentScene by collectLastValue(underTest.currentScene)
             val transitionTo by collectLastValue(underTest.transitioningTo)
             kosmos.sceneContainerRepository.setTransitionState(
@@ -151,39 +131,30 @@
         }
 
     @Test
+    fun changeScene_toHomeSceneFamily() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+
+            underTest.changeScene(SceneFamilies.Home, "reason")
+            runCurrent()
+
+            assertThat(currentScene).isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene.value)
+        }
+
+    @Test
     fun snapToScene_toUnknownScene_doesNothing() =
         testScope.runTest {
-            val sceneKeys =
-                listOf(
-                    Scenes.QuickSettings,
-                    Scenes.Shade,
-                    Scenes.Lockscreen,
-                    Scenes.Gone,
-                    Scenes.Communal,
-                )
-            val navigationDistances =
-                mapOf(
-                    Scenes.Gone to 0,
-                    Scenes.Lockscreen to 0,
-                    Scenes.Communal to 1,
-                    Scenes.Shade to 2,
-                    Scenes.QuickSettings to 3,
-                )
-            kosmos.sceneContainerConfig =
-                SceneContainerConfig(sceneKeys, Scenes.Lockscreen, navigationDistances)
-            underTest = kosmos.sceneInteractor
             val currentScene by collectLastValue(underTest.currentScene)
             val previousScene = currentScene
-            assertThat(previousScene).isNotEqualTo(Scenes.Bouncer)
-            underTest.snapToScene(Scenes.Bouncer, "reason")
+            val unknownScene = SceneKey("UNKNOWN")
+            assertThat(previousScene).isNotEqualTo(unknownScene)
+            underTest.snapToScene(unknownScene, "reason")
             assertThat(currentScene).isEqualTo(previousScene)
         }
 
     @Test
     fun snapToScene() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
-
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -194,8 +165,6 @@
     @Test
     fun snapToScene_toGoneWhenUnl_doesNotThrow() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
-
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -210,15 +179,22 @@
 
     @Test(expected = IllegalStateException::class)
     fun snapToScene_toGoneWhenStillLocked_throws() =
+        testScope.runTest { underTest.snapToScene(Scenes.Gone, "reason") }
+
+    @Test
+    fun snapToScene_toHomeSceneFamily() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
-            underTest.snapToScene(Scenes.Gone, "reason")
+            val currentScene by collectLastValue(underTest.currentScene)
+
+            underTest.snapToScene(SceneFamilies.Home, "reason")
+            runCurrent()
+
+            assertThat(currentScene).isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene.value)
         }
 
     @Test
     fun sceneChanged_inDataSource() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
@@ -230,14 +206,14 @@
     @Test
     fun transitionState() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
-            val underTest = kosmos.sceneContainerRepository
+            val sceneContainerRepository = kosmos.sceneContainerRepository
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(Scenes.Lockscreen)
                 )
-            underTest.setTransitionState(transitionState)
-            val reflectedTransitionState by collectLastValue(underTest.transitionState)
+            sceneContainerRepository.setTransitionState(transitionState)
+            val reflectedTransitionState by
+                collectLastValue(sceneContainerRepository.transitionState)
             assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
 
             val progress = MutableStateFlow(1f)
@@ -258,7 +234,7 @@
             progress.value = 0.9f
             assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
 
-            underTest.setTransitionState(null)
+            sceneContainerRepository.setTransitionState(null)
             assertThat(reflectedTransitionState)
                 .isEqualTo(
                     ObservableTransitionState.Idle(kosmos.sceneContainerConfig.initialSceneKey)
@@ -268,7 +244,6 @@
     @Test
     fun transitioningTo() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(underTest.currentScene.value)
@@ -306,7 +281,6 @@
     @Test
     fun isTransitionUserInputOngoing_idle_false() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Idle(Scenes.Shade)
@@ -321,7 +295,6 @@
     @Test
     fun isTransitionUserInputOngoing_transition_true() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Transition(
@@ -343,7 +316,6 @@
     @Test
     fun isTransitionUserInputOngoing_updateMidTransition_false() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Transition(
@@ -377,7 +349,6 @@
     @Test
     fun isTransitionUserInputOngoing_updateOnIdle_false() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             val transitionState =
                 MutableStateFlow<ObservableTransitionState>(
                     ObservableTransitionState.Transition(
@@ -403,7 +374,6 @@
     @Test
     fun isVisible() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             val isVisible by collectLastValue(underTest.isVisible)
             assertThat(isVisible).isTrue()
 
@@ -417,7 +387,6 @@
     @Test
     fun isVisible_duringRemoteUserInteraction_forcedVisible() =
         testScope.runTest {
-            underTest = kosmos.sceneInteractor
             underTest.setVisible(false, "reason")
             val isVisible by collectLastValue(underTest.isVisible)
             assertThat(isVisible).isFalse()
@@ -428,4 +397,57 @@
 
             assertThat(isVisible).isFalse()
         }
+
+    @Test
+    fun resolveSceneFamily_home() =
+        testScope.runTest {
+            assertThat(underTest.resolveSceneFamily(SceneFamilies.Home).first())
+                .isEqualTo(kosmos.homeSceneFamilyResolver.resolvedScene.value)
+        }
+
+    @Test
+    fun resolveSceneFamily_nonFamily() =
+        testScope.runTest {
+            val resolved = underTest.resolveSceneFamily(Scenes.Gone).toList()
+            assertThat(resolved).containsExactly(Scenes.Gone).inOrder()
+        }
+
+    @Test
+    fun transitionValue_test_idle() =
+        testScope.runTest {
+            val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone))
+
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
+            assertThat(transitionValue).isEqualTo(1f)
+
+            kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
+            assertThat(transitionValue).isEqualTo(0f)
+        }
+
+    @Test
+    fun transitionValue_test_transitions() =
+        testScope.runTest {
+            val transitionValue by collectLastValue(underTest.transitionProgress(Scenes.Gone))
+            val progress = MutableStateFlow(0f)
+
+            kosmos.setSceneTransition(
+                Transition(from = Scenes.Lockscreen, to = Scenes.Gone, progress = progress)
+            )
+            assertThat(transitionValue).isEqualTo(0f)
+
+            progress.value = 0.4f
+            assertThat(transitionValue).isEqualTo(0.4f)
+
+            kosmos.setSceneTransition(
+                Transition(from = Scenes.Gone, to = Scenes.Lockscreen, progress = progress)
+            )
+            progress.value = 0.7f
+            assertThat(transitionValue).isEqualTo(0.3f)
+
+            kosmos.setSceneTransition(
+                Transition(from = Scenes.Lockscreen, to = Scenes.Shade, progress = progress)
+            )
+            progress.value = 0.9f
+            assertThat(transitionValue).isEqualTo(0f)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index ac66e66..e40c8ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class)
+@file:OptIn(ExperimentalCoroutinesApi::class)
 
 package com.android.systemui.scene.domain.startable
 
@@ -395,6 +395,7 @@
                 )
             assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
             underTest.start()
+            runCurrent()
 
             kosmos.fakePowerRepository.updateWakefulness(
                 rawState = WakefulnessState.STARTING_TO_SLEEP,
@@ -1285,6 +1286,42 @@
         }
 
     @Test
+    fun switchToGone_whenSurfaceBehindLockscreenVisibleMidTransition() =
+        testScope.runTest {
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val transitionStateFlow =
+                prepareState(
+                    authenticationMethod = AuthenticationMethodModel.None,
+                )
+            underTest.start()
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            // Swipe to Gone, more than halfway
+            transitionStateFlow.value =
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Lockscreen,
+                    toScene = Scenes.Gone,
+                    currentScene = flowOf(Scenes.Gone),
+                    progress = flowOf(0.51f),
+                    isInitiatedByUserInput = true,
+                    isUserInputOngoing = flowOf(true),
+                )
+            runCurrent()
+            // Lift finger
+            transitionStateFlow.value =
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Lockscreen,
+                    toScene = Scenes.Gone,
+                    currentScene = flowOf(Scenes.Gone),
+                    progress = flowOf(0.51f),
+                    isInitiatedByUserInput = true,
+                    isUserInputOngoing = flowOf(false),
+                )
+            runCurrent()
+
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
     fun switchToGone_extendUnlock() =
         testScope.runTest {
             val currentScene by collectLastValue(sceneInteractor.currentScene)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
index 468c39d..fa4da42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImplTest.kt
@@ -30,12 +30,14 @@
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shared.recents.utilities.Utilities
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -77,6 +79,8 @@
     @Test
     fun animateCollapseQs_fullyCollapse_entered() =
         testScope.runTest {
+            // Ensure that HomeSceneFamilyResolver is running
+            kosmos.homeSceneFamilyResolver.resolvedScene.launchIn(backgroundScope)
             val actual by collectLastValue(sceneInteractor.currentScene)
             enterDevice()
             setScene(Scenes.QuickSettings)
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 f89f18a..3ded8a3 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
@@ -6,15 +6,27 @@
 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.SceneKey
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.activityStarter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 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
 import com.android.systemui.util.mockito.argThat
 import com.google.common.truth.Truth.assertThat
+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.Test
@@ -24,12 +36,16 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
 class ShadeHeaderViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val mobileIconsInteractor = kosmos.fakeMobileIconsInteractor
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val deviceEntryInteractor = kosmos.deviceEntryInteractor
 
     private val underTest: ShadeHeaderViewModel = kosmos.shadeHeaderViewModel
 
@@ -77,6 +93,30 @@
                 )
         }
 
+    @Test
+    fun onSystemIconContainerClicked_locked_collapsesShadeToLockscreen() =
+        testScope.runTest {
+            setDeviceEntered(false)
+            setScene(Scenes.Shade)
+
+            underTest.onSystemIconContainerClicked()
+            runCurrent()
+
+            assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun onSystemIconContainerClicked_unlocked_collapsesShadeToGone() =
+            testScope.runTest {
+                setDeviceEntered(true)
+                setScene(Scenes.Shade)
+
+                underTest.onSystemIconContainerClicked()
+                runCurrent()
+
+                assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
+            }
+
     companion object {
         private val SUB_1 =
             SubscriptionModel(
@@ -93,6 +133,32 @@
                 profileClass = PROFILE_CLASS_UNSET,
             )
     }
+
+    private fun setScene(key: SceneKey) {
+        sceneInteractor.changeScene(key, "test")
+        sceneInteractor.setTransitionState(
+                MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+        )
+        testScope.runCurrent()
+    }
+
+    private fun TestScope.setDeviceEntered(isEntered: Boolean) {
+        if (isEntered) {
+            // Unlock the device marking the device has entered.
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                    SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+        }
+        setScene(
+                if (isEntered) {
+                    Scenes.Gone
+                } else {
+                    Scenes.Lockscreen
+                }
+        )
+        assertThat(deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
+    }
 }
 
 private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 482dc5d..c53cdf8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -19,6 +19,8 @@
 import android.testing.TestableLooper
 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.SceneKey
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.systemui.SysuiTestCase
@@ -41,6 +43,8 @@
 import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModel
@@ -56,7 +60,9 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.Locale
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -75,7 +81,6 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
     private val shadeRepository by lazy { kosmos.shadeRepository }
 
     private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
@@ -91,7 +96,6 @@
         underTest =
             ShadeSceneViewModel(
                 applicationScope = testScope.backgroundScope,
-                deviceEntryInteractor = deviceEntryInteractor,
                 shadeHeaderViewModel = kosmos.shadeHeaderViewModel,
                 qsSceneAdapter = qsSceneAdapter,
                 notifications = kosmos.notificationsPlaceholderViewModel,
@@ -109,33 +113,36 @@
     fun upTransitionSceneKey_deviceLocked_lockScreen() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Pin
             )
 
             assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(Scenes.Lockscreen)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
         }
 
     @Test
     fun upTransitionSceneKey_deviceUnlocked_gone() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Pin
             )
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
+            setDeviceEntered(true)
 
             assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(Scenes.Gone)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
         }
 
     @Test
     fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.None
@@ -143,13 +150,15 @@
             sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
 
             assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(Scenes.Lockscreen)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
         }
 
     @Test
     fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
         testScope.runTest {
             val destinationScenes by collectLastValue(underTest.destinationScenes)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
             kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.None
@@ -158,7 +167,8 @@
             sceneInteractor.changeScene(Scenes.Gone, "reason")
 
             assertThat(destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(Scenes.Gone)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
         }
 
     @Test
@@ -189,9 +199,7 @@
             kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                 AuthenticationMethodModel.Pin
             )
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
+            setDeviceEntered(true)
             runCurrent()
 
             assertThat(isClickable).isFalse()
@@ -338,6 +346,32 @@
         return maxTranslation
     }
 
+    private fun TestScope.setDeviceEntered(isEntered: Boolean) {
+        if (isEntered) {
+            // Unlock the device marking the device has entered.
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+        }
+        setScene(
+            if (isEntered) {
+                Scenes.Gone
+            } else {
+                Scenes.Lockscreen
+            }
+        )
+        assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
+    }
+
+    private fun TestScope.setScene(key: SceneKey) {
+        sceneInteractor.changeScene(key, "test")
+        sceneInteractor.setTransitionState(
+            MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+        )
+        runCurrent()
+    }
+
     private data class Translations(
         val start: Float,
         val end: Float,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
index ad4b98b..eac86e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
@@ -20,6 +20,7 @@
 import android.app.smartspace.SmartspaceTarget
 import android.content.Context
 import android.graphics.drawable.Drawable
+import android.os.Handler
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.FrameLayout
@@ -86,6 +87,8 @@
 
         override fun setUiSurface(uiSurface: String) {}
 
+        override fun setBgHandler(bgHandler: Handler?) {}
+
         override fun setDozeAmount(amount: Float) {}
 
         override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
index 3a38631..e774aed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
@@ -20,6 +20,7 @@
 import android.app.smartspace.SmartspaceTarget
 import android.content.Context
 import android.graphics.drawable.Drawable
+import android.os.Handler
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
@@ -119,6 +120,8 @@
 
         override fun setUiSurface(uiSurface: String) {}
 
+        override fun setBgHandler(bgHandler: Handler?) {}
+
         override fun setDozeAmount(amount: Float) {}
 
         override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
index c4506f2..cc3fdc5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt
@@ -32,6 +32,8 @@
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_DELAYED_STACK_FADE_IN
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationTransitionThresholds.EXPANSION_FOR_MAX_SCRIM_ALPHA
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationScrollViewModel
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
 import com.android.systemui.testKosmos
@@ -121,10 +123,13 @@
             sceneInteractor.setTransitionState(transitionState)
             val expandFraction by collectLastValue(scrollViewModel.expandFraction)
             assertThat(expandFraction).isEqualTo(0f)
+
+            fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
             val isScrollable by collectLastValue(scrollViewModel.isScrollable)
             assertThat(isScrollable).isFalse()
 
             fakeSceneDataSource.pause()
+
             sceneInteractor.changeScene(Scenes.Shade, "reason")
             val transitionProgress = MutableStateFlow(0f)
             transitionState.value =
@@ -159,8 +164,10 @@
             sceneInteractor.setTransitionState(transitionState)
             val expandFraction by collectLastValue(scrollViewModel.expandFraction)
             assertThat(expandFraction).isEqualTo(1f)
+
+            fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen)
             val isScrollable by collectLastValue(scrollViewModel.isScrollable)
-            assertThat(isScrollable).isFalse()
+            assertThat(isScrollable).isTrue()
         }
 
     @Test
@@ -203,4 +210,50 @@
             assertThat(expandFraction).isEqualTo(1f)
             assertThat(isScrollable).isFalse()
         }
+
+    @Test
+    fun shadeExpansion_goneToQs() =
+        testScope.runTest {
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(currentScene = Scenes.Gone)
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            val expandFraction by collectLastValue(scrollViewModel.expandFraction)
+            assertThat(expandFraction).isEqualTo(0f)
+
+            fakeSceneDataSource.changeScene(toScene = Scenes.Gone)
+            val isScrollable by collectLastValue(scrollViewModel.isScrollable)
+            assertThat(isScrollable).isFalse()
+
+            fakeSceneDataSource.pause()
+
+            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
+            val transitionProgress = MutableStateFlow(0f)
+            transitionState.value =
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Gone,
+                    toScene = Scenes.QuickSettings,
+                    currentScene = flowOf(Scenes.QuickSettings),
+                    progress = transitionProgress,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                )
+            val steps = 10
+            repeat(steps) { repetition ->
+                val progress = (1f / steps) * (repetition + 1)
+                transitionProgress.value = progress
+                runCurrent()
+                assertThat(expandFraction)
+                    .isEqualTo(
+                        (progress / EXPANSION_FOR_MAX_SCRIM_ALPHA -
+                                EXPANSION_FOR_DELAYED_STACK_FADE_IN)
+                            .coerceIn(0f, 1f)
+                    )
+            }
+
+            fakeSceneDataSource.unpause(expectedScene = Scenes.QuickSettings)
+            assertThat(expandFraction).isEqualTo(1f)
+            assertThat(isScrollable).isFalse()
+        }
 }
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 c35c165..f14c96ded 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
@@ -23,11 +23,15 @@
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
 import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
 import com.android.systemui.common.shared.model.NotificationContainerBounds
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.flags.BrokenWithSceneContainer
@@ -42,6 +46,7 @@
 import com.android.systemui.keyguard.shared.model.BurnInModel
 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.GONE
 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
@@ -133,6 +138,9 @@
     val largeScreenHeaderHelper
         get() = kosmos.mockLargeScreenHeaderHelper
 
+    val communalSceneRepository
+        get() = kosmos.communalSceneRepository
+
     lateinit var underTest: SharedNotificationContainerViewModel
 
     @Before
@@ -842,6 +850,28 @@
         }
 
     @Test
+    @DisableSceneContainer
+    fun updateBounds_fromGone_withoutTransitions() =
+        testScope.runTest {
+            // Start step is already at 1.0
+            val runningStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.RUNNING)
+            val finishStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.FINISHED)
+
+            val bounds by collectLastValue(underTest.bounds)
+            val top = 123f
+            val bottom = 456f
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(runningStep)
+            runCurrent()
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep)
+            runCurrent()
+            keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
+            runCurrent()
+
+            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
+        }
+
+    @Test
     fun alphaOnFullQsExpansion() =
         testScope.runTest {
             val viewState = ViewStateAccessor()
@@ -995,6 +1025,230 @@
             assertThat(fadeIn[0]).isEqualTo(false)
         }
 
+    @Test
+    @BrokenWithSceneContainer(330311871)
+    fun alpha_isZero_fromPrimaryBouncerToGoneWhileCommunalSceneVisible() =
+        testScope.runTest {
+            val viewState = ViewStateAccessor()
+            val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+            showPrimaryBouncer()
+            showCommunalScene()
+
+            // PRIMARY_BOUNCER->GONE transition is started
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.STARTED,
+                    value = 0f,
+                )
+            )
+            runCurrent()
+
+            // PRIMARY_BOUNCER->GONE transition running
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.RUNNING,
+                    value = 0.1f,
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.RUNNING,
+                    value = 0.9f,
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+
+            hideCommunalScene()
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.FINISHED,
+                    value = 1f
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
+    @BrokenWithSceneContainer(330311871)
+    fun alpha_fromPrimaryBouncerToGoneWhenCommunalSceneNotVisible() =
+        testScope.runTest {
+            val viewState = ViewStateAccessor()
+            val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+            showPrimaryBouncer()
+            hideCommunalScene()
+
+            // PRIMARY_BOUNCER->GONE transition is started
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            runCurrent()
+
+            // PRIMARY_BOUNCER->GONE transition running
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.RUNNING,
+                    value = 0.1f,
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isIn(Range.closedOpen(0f, 1f))
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.RUNNING,
+                    value = 0.9f,
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isIn(Range.closedOpen(0f, 1f))
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.PRIMARY_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.FINISHED,
+                    value = 1f
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
+    @BrokenWithSceneContainer(330311871)
+    fun alpha_isZero_fromAlternateBouncerToGoneWhileCommunalSceneVisible() =
+        testScope.runTest {
+            val viewState = ViewStateAccessor()
+            val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+            showAlternateBouncer()
+            showCommunalScene()
+
+            // ALTERNATE_BOUNCER->GONE transition is started
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.STARTED,
+                    value = 0f,
+                )
+            )
+            runCurrent()
+
+            // ALTERNATE_BOUNCER->GONE transition running
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.RUNNING,
+                    value = 0.1f,
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.RUNNING,
+                    value = 0.9f,
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+
+            hideCommunalScene()
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.FINISHED,
+                    value = 1f
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
+    @BrokenWithSceneContainer(330311871)
+    fun alpha_fromAlternateBouncerToGoneWhenCommunalSceneNotVisible() =
+        testScope.runTest {
+            val viewState = ViewStateAccessor()
+            val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+            showAlternateBouncer()
+            hideCommunalScene()
+
+            // ALTERNATE_BOUNCER->GONE transition is started
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+            runCurrent()
+
+            // ALTERNATE_BOUNCER->GONE transition running
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.RUNNING,
+                    value = 0.1f,
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isIn(Range.closedOpen(0f, 1f))
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.RUNNING,
+                    value = 0.9f,
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isIn(Range.closedOpen(0f, 1f))
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    to = GONE,
+                    transitionState = TransitionState.FINISHED,
+                    value = 1f
+                )
+            )
+            runCurrent()
+            assertThat(alpha).isEqualTo(0f)
+        }
+
     private suspend fun TestScope.showLockscreen() {
         shadeTestUtil.setQsExpansion(0f)
         shadeTestUtil.setLockscreenShadeExpansion(0f)
@@ -1046,4 +1300,52 @@
             testScope,
         )
     }
+
+    private suspend fun TestScope.showPrimaryBouncer() {
+        shadeTestUtil.setQsExpansion(0f)
+        shadeTestUtil.setLockscreenShadeExpansion(0f)
+        runCurrent()
+        keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+        runCurrent()
+        kosmos.keyguardBouncerRepository.setPrimaryShow(true)
+        runCurrent()
+        keyguardTransitionRepository.sendTransitionSteps(
+            from = KeyguardState.GLANCEABLE_HUB,
+            to = KeyguardState.PRIMARY_BOUNCER,
+            testScope,
+        )
+    }
+
+    private suspend fun TestScope.showAlternateBouncer() {
+        shadeTestUtil.setQsExpansion(0f)
+        shadeTestUtil.setLockscreenShadeExpansion(0f)
+        runCurrent()
+        keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+        runCurrent()
+        kosmos.keyguardBouncerRepository.setPrimaryShow(false)
+        runCurrent()
+        keyguardTransitionRepository.sendTransitionSteps(
+            from = KeyguardState.GLANCEABLE_HUB,
+            to = KeyguardState.ALTERNATE_BOUNCER,
+            testScope,
+        )
+    }
+
+    private fun TestScope.showCommunalScene() {
+        val transitionState =
+            MutableStateFlow<ObservableTransitionState>(
+                ObservableTransitionState.Idle(CommunalScenes.Communal)
+            )
+        communalSceneRepository.setTransitionState(transitionState)
+        runCurrent()
+    }
+
+    private fun TestScope.hideCommunalScene() {
+        val transitionState =
+            MutableStateFlow<ObservableTransitionState>(
+                ObservableTransitionState.Idle(CommunalScenes.Blank)
+            )
+        communalSceneRepository.setTransitionState(transitionState)
+        runCurrent()
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index 57d3251..1656a2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -60,6 +60,7 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -132,6 +133,7 @@
                 communalSceneInteractor = communalSceneInteractor,
             )
         whenever(userTracker.userHandle).thenReturn(UserHandle.OWNER)
+        whenever(communalSceneInteractor.isIdleOnCommunal).thenReturn(MutableStateFlow(false))
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 88bef91..206b39c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -627,7 +627,7 @@
         hum.onEntryAdded(entryToPin);
 
         assertEquals(2, mUiEventLoggerFake.numLogs());
-        assertEquals(AvalancheController.ThrottleEvent.SHOWN.getId(),
+        assertEquals(AvalancheController.ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN.getId(),
                 mUiEventLoggerFake.eventId(0));
         assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
                 mUiEventLoggerFake.eventId(1));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index 10a4eb7..7385a47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
@@ -71,6 +71,7 @@
                 addOverride(R.drawable.ic_headphone, testIcon)
                 addOverride(R.drawable.ic_smartphone, testIcon)
                 addOverride(R.drawable.ic_media_speaker_device, testIcon)
+                addOverride(R.drawable.ic_media_tablet, testIcon)
 
                 addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
index 8921a23..0f56d0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
@@ -65,6 +65,7 @@
 
             with(context.orCreateTestableResources) {
                 addOverride(R.drawable.ic_smartphone, testIcon)
+                addOverride(R.drawable.ic_media_tablet, testIcon)
 
                 addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
                 addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 83658d3..074277c 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -24,6 +24,7 @@
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
 import android.os.Parcelable;
 import android.util.Log;
 import android.view.View;
@@ -123,6 +124,9 @@
          */
         void setUiSurface(String uiSurface);
 
+        /** Set background handler to make binder calls. */
+        void setBgHandler(Handler bgHandler);
+
         /**
          * Range [0.0 - 1.0] when transitioning from Lockscreen to/from AOD
          */
@@ -134,6 +138,12 @@
         default void setScreenOn(boolean screenOn) {}
 
         /**
+         * Sets a delegate to handle clock event registration. Should be called immediately after
+         * the view is created.
+         */
+        default void setTimeChangedDelegate(TimeChangedDelegate delegate) {}
+
+        /**
          * Set if dozing is true or false
          */
         default void setDozing(boolean dozing) {}
@@ -228,4 +238,13 @@
         /** Start the PendingIntent */
         void startPendingIntent(View v, PendingIntent pi, boolean showOnLockscreen);
     }
+
+    /** Interface for delegating time updates */
+    interface TimeChangedDelegate {
+        /** Register the callback to be called when time is updated **/
+        void register(Runnable callback);
+
+        /** Unegister the callback **/
+        void unregister();
+    }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index de65931..7cf56aa 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -58,6 +58,19 @@
             @Nullable ActivityTransitionAnimator.Controller animationController);
 
     /**
+     * Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable,
+     * ActivityTransitionAnimator.Controller)} but will always not dismiss the keyguard when
+     * launching activities. This should be avoided and other alternatives should be used.
+     */
+    void startPendingIntentWithoutDismissing(
+            PendingIntent intent,
+            boolean dismissShade,
+            Runnable intentSentUiThreadCallback,
+            @Nullable ActivityTransitionAnimator.Controller animationController,
+            @Nullable Intent fillInIntent,
+            @Nullable Bundle extraOptions);
+
+    /**
      * Similar to {@link #startPendingIntentDismissingKeyguard}, except that it supports launching
      * activities on top of the keyguard. If the activity supports {@code showOverLockscreen}, it
      * will show over keyguard without first dimissing it. If it doesn't support it, calling this
diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml
index de53f9f..544adee 100644
--- a/packages/SystemUI/res-keyguard/values-ky/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml
@@ -23,7 +23,7 @@
     <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"PIN кодуңузду киргизиңиз"</string>
     <string name="keyguard_enter_pin" msgid="8114529922480276834">"PIN кодду киргизиңиз"</string>
     <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Графикалык ачкычты тартыңыз"</string>
-    <string name="keyguard_enter_pattern" msgid="7616595160901084119">"Графикалык ачкчты тартңыз"</string>
+    <string name="keyguard_enter_pattern" msgid="7616595160901084119">"Графикалык ачкычты тартыңыз"</string>
     <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Сырсөзүңүздү киргизиңиз"</string>
     <string name="keyguard_enter_password" msgid="6483623792371009758">"Сырсөздү киргизиңиз"</string>
     <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"SIM-карта жараксыз."</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index f0d9596..3411571 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -25,7 +25,7 @@
     <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"ਆਪਣਾ ਪੈਟਰਨ ਦਾਖਲ ਕਰੋ"</string>
     <string name="keyguard_enter_pattern" msgid="7616595160901084119">"ਪੈਟਰਨ ਬਣਾਓ"</string>
     <string name="keyguard_enter_your_password" msgid="7225626204122735501">"ਆਪਣਾ ਪਾਸਵਰਡ ਦਾਖਲ ਕਰੋ"</string>
-    <string name="keyguard_enter_password" msgid="6483623792371009758">"ਪਾਸਵਰਡ ਦਾਖਲ ਕਰੋ"</string>
+    <string name="keyguard_enter_password" msgid="6483623792371009758">"ਪਾਸਵਰਡ ਪਾਓ"</string>
     <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"ਅਵੈਧ ਕਾਰਡ।"</string>
     <string name="keyguard_charged" msgid="5478247181205188995">"ਚਾਰਜ ਹੋ ਗਿਆ"</string>
     <string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml
new file mode 100644
index 0000000..d949200
--- /dev/null
+++ b/packages/SystemUI/res/drawable/android15_patch_adaptive.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.
+-->
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@drawable/android15_patch_adaptive_background"/>
+    <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/>
+    <monochrome android:drawable="@drawable/android15_patch_monochrome"/>
+</adaptive-icon>
diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml
new file mode 100644
index 0000000..d4850d3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <!-- space (themed version) -->
+    <path
+        android:pathData="M0,0h108v108h-108z"
+        android:fillColor="@android:color/system_neutral1_800"/>
+    <!-- stars (themed version) -->
+    <group>
+        <path
+            android:pathData="M32,34 h1v1h-1z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M33,61 h1v1h-1z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M71,34 h1v1h-1z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M62,56 h1v1h-1z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M68,47 h1v1h-1z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M72,55 h1v1h-1z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M39,36 h1v1h-1z"
+            android:fillColor="@android:color/system_accent3_200"/>
+
+        <path
+            android:pathData="M72,49 h2v2h-2z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M46,53 h2v2h-2z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M32,45 h2v2h-2z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M43,37 h2v2h-2z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M78,51 h2v2h-2z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M34,51 h2v2h-2z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M76,41 h2v2h-2z"
+            android:fillColor="@android:color/system_accent3_200"/>
+        <path
+            android:pathData="M39,46 h2v2h-2z"
+            android:fillColor="@android:color/system_accent3_200"/>
+  </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml
new file mode 100644
index 0000000..34f6ee0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml
@@ -0,0 +1,60 @@
+<?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="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+
+    <!-- zoomies (themed version) -->
+    <group>
+        <path
+            android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z"
+            android:fillColor="@android:color/system_accent1_100"
+            android:fillType="evenOdd"/>
+        <path
+            android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z"
+            android:fillColor="#ffffff"
+            android:fillType="evenOdd"/>
+        <path
+            android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0"
+            android:fillColor="#ffffff"/>
+    </group>
+    <group>
+        <!-- head! it's like sputnik -->
+        <path
+            android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0"
+            android:fillColor="#34A853"/>
+        <!-- ant -->
+        <path
+            android:pathData="M38,63.5L44.5,74.5"
+            android:strokeWidth="5"
+            android:fillColor="#00000000"
+            android:strokeColor="#34A853"
+            android:strokeLineCap="round"/>
+        <path
+            android:pathData="M70,63.5L63.5,74.5"
+            android:strokeWidth="5"
+            android:fillColor="#00000000"
+            android:strokeColor="#34A853"
+            android:strokeLineCap="round"/>
+    </group>
+    <!-- spaceship -->
+    <path
+        android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z"
+        android:fillColor="#E9F3EB"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/android15_patch_monochrome.xml b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml
new file mode 100644
index 0000000..a91cc86
--- /dev/null
+++ b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml
@@ -0,0 +1,119 @@
+<?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="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+  <group>
+    <path
+        android:pathData="
+        M54,94.25
+        m-26.25,0
+        a26.25,26.25 0,1 1,52.5 0
+        a26.25,26.25 0,1 1,-52.5 0
+        "
+        android:fillColor="#000000"/>
+    <path
+        android:pathData="M38,63.5L44.5,74.5"
+        android:strokeWidth="5"
+        android:fillColor="#00000000"
+        android:strokeColor="#000000"
+        android:strokeLineCap="round"/>
+    <path
+        android:pathData="M70,63.5L63.5,74.5"
+        android:strokeWidth="5"
+        android:fillColor="#00000000"
+        android:strokeColor="#000000"
+        android:strokeLineCap="round"/>
+
+    <path
+        android:pathData="
+        M54,34
+        C52.34,34 51,35.29 51,36.88
+        V40.44
+        C51,40.75 51.25,41 51.56,41
+        C51.87,41 52.13,40.75 52.13,40.44
+        V39.48
+        C52.13,38.87 52.63,38.37 53.25,38.37
+        H54.75
+        C55.37,38.37 55.87,38.87 55.87,39.48
+        V40.44
+        C55.87,40.75 56.13,41 56.44,41
+        C56.75,41 57,40.75 57,40.44
+        V36.88
+        C57,35.29 55.66,34 54,34
+        H54
+        Z
+        "
+        android:fillColor="#34A853"/>
+    <path
+        android:strokeWidth="1"
+        android:pathData="M54,40V67"
+        android:fillColor="#00000000"
+        android:strokeColor="#40FFFFFF"
+        />
+
+    <path
+        android:pathData="M32,34 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M33,61 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M71,34 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M62,56 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M68,47 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M72,55 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M39,36 h1v1h-1z"
+        android:fillColor="#ffffff"/>
+
+    <path
+        android:pathData="M72,49 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M46,53 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M32,45 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M43,37 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M78,51 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M34,51 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M76,41 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M39,46 h2v2h-2z"
+        android:fillColor="#ffffff"/>
+
+  </group>
+</vector>
diff --git a/core/res/res/drawable/immersive_cling_light_bg_circ.xml b/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml
similarity index 60%
copy from core/res/res/drawable/immersive_cling_light_bg_circ.xml
copy to packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml
index df5d5ad..dd3d9e3 100644
--- a/core/res/res/drawable/immersive_cling_light_bg_circ.xml
+++ b/packages/SystemUI/res/drawable/ic_bt_le_audio_sharing_18dp.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
-  ~ Copyright (C) 2015 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.
@@ -12,15 +12,11 @@
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
+  ~ limitations under the License.
   -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-
-    <solid android:color="#80ffffff" />
-
-    <size
-        android:height="76dp"
-        android:width="76dp" />
-
-</shape>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:drawable="@drawable/ic_bt_le_audio_sharing"
+        android:width="18dp"
+        android:height="18dp" />
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_swipe_down.xml b/packages/SystemUI/res/drawable/ic_swipe_down.xml
new file mode 100644
index 0000000..15712d6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_swipe_down.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="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M180,600L40,460L82,418L152,488Q146,461 143,434Q140,407 140,380Q140,298 167,221Q194,144 245,80L288,123Q245,179 222.5,244.5Q200,310 200,380Q200,406 203,431.5Q206,457 213,482L278,418L320,460L180,600ZM658,833Q635,841 611.5,840.5Q588,840 566,829L304,707L322,667Q332,647 350,634.5Q368,622 390,620L458,615L346,308Q340,292 347,277.5Q354,263 370,257Q386,251 400.5,258Q415,265 421,281L569,688L469,695L600,756Q607,759 615,759.5Q623,760 630,758L787,701Q818,690 832,659.5Q846,629 835,598L780,448Q774,432 781,417.5Q788,403 804,397Q820,391 834.5,398Q849,405 855,421L910,571Q933,634 905.5,693.5Q878,753 815,776L658,833ZM568,568L514,417Q508,401 515,386.5Q522,372 538,366Q554,360 568.5,367Q583,374 589,390L644,540L568,568ZM681,527L640,414Q634,398 641,383.5Q648,369 664,363Q680,357 694.5,364Q709,371 715,387L756,499L681,527ZM689,605L689,605L689,605Q689,605 689,605Q689,605 689,605L689,605Q689,605 689,605Q689,605 689,605L689,605L689,605Z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/immersive_cling_light_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_bg.xml
similarity index 67%
copy from core/res/res/drawable/immersive_cling_light_bg_circ.xml
copy to packages/SystemUI/res/drawable/immersive_cling_bg.xml
index df5d5ad..de29c32 100644
--- a/core/res/res/drawable/immersive_cling_light_bg_circ.xml
+++ b/packages/SystemUI/res/drawable/immersive_cling_bg.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2015 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.
@@ -15,12 +15,10 @@
   ~ limitations under the License
   -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-
-    <solid android:color="#80ffffff" />
-
-    <size
-        android:height="76dp"
-        android:width="76dp" />
-
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners
+        android:bottomLeftRadius="28dp"
+        android:bottomRightRadius="28dp"/>
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml
deleted file mode 100644
index 32e88ab..0000000
--- a/packages/SystemUI/res/drawable/immersive_cling_bg_circ.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-
-    <solid android:color="?android:attr/colorBackground" />
-
-    <size
-        android:height="56dp"
-        android:width="56dp" />
-
-</shape>
diff --git a/packages/SystemUI/res/drawable/immersive_cling_btn_bg.xml b/packages/SystemUI/res/drawable/immersive_cling_btn_bg.xml
new file mode 100644
index 0000000..df49e38
--- /dev/null
+++ b/packages/SystemUI/res/drawable/immersive_cling_btn_bg.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.
+  -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <ripple android:color="?android:attr/colorControlHighlight">
+        <item android:id="@android:id/mask">
+            <shape android:shape="rectangle">
+                <solid android:color="@android:color/white" />
+                <corners android:radius="28dp" />
+            </shape>
+        </item>
+        <item>
+            <shape android:shape="rectangle">
+                <corners android:radius="28dp" />
+                <solid android:color="?android:attr/colorAccent" />
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml b/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml
deleted file mode 100644
index 12c3e23..0000000
--- a/packages/SystemUI/res/drawable/immersive_cling_light_bg_circ.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-
-    <solid android:color="?android:attr/colorBackground" />
-
-    <size
-        android:height="76dp"
-        android:width="76dp" />
-
-</shape>
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index 5ce2601..08edf59 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -13,9 +13,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:sysui="http://schemas.android.com/apk/res-auto"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/volume_dialog_container"
     android:layout_width="wrap_content"
@@ -97,16 +95,18 @@
                     android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
                     android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
                     android:paddingRight="@dimen/volume_dialog_ringer_rows_padding">
+
                     <com.android.keyguard.AlphaOptimizedImageButton
                         android:id="@+id/settings"
-                        android:src="@drawable/horizontal_ellipsis"
                         android:layout_width="@dimen/volume_dialog_tap_target_size"
                         android:layout_height="@dimen/volume_dialog_tap_target_size"
                         android:layout_gravity="center"
-                        android:contentDescription="@string/accessibility_volume_settings"
                         android:background="@drawable/ripple_drawable_20dp"
-                        android:tint="?androidprv:attr/colorAccent"
-                        android:soundEffectsEnabled="false" />
+                        android:contentDescription="@string/accessibility_volume_settings"
+                        android:scaleType="centerInside"
+                        android:soundEffectsEnabled="false"
+                        android:src="@drawable/horizontal_ellipsis"
+                        android:tint="?androidprv:attr/colorAccent" />
                 </FrameLayout>
             </LinearLayout>
 
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/ambient_status_bar_view.xml
similarity index 97%
rename from packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
rename to packages/SystemUI/res/layout/ambient_status_bar_view.xml
index ec2edb5..7d765ce 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/ambient_status_bar_view.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.dreams.DreamOverlayStatusBarView
+<com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/dream_overlay_status_bar"
@@ -118,4 +118,4 @@
             android:contentDescription="@string/assistant_attention_content_description" />
 
     </LinearLayout>
-</com.android.systemui.dreams.DreamOverlayStatusBarView>
+</com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView>
diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml
index 2a1ce1f..cc5a27d 100644
--- a/packages/SystemUI/res/layout/auth_container_view.xml
+++ b/packages/SystemUI/res/layout/auth_container_view.xml
@@ -28,9 +28,9 @@
 
     <View
         android:id="@+id/panel"
+        style="@style/AuthNonCredentialPanelStyle"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="?androidprv:attr/materialColorSurfaceContainer"
         android:elevation="@dimen/biometric_dialog_elevation"/>
 
     <ScrollView
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index a598007..27b8006 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -268,6 +268,12 @@
                 android:ellipsize="end"
                 android:maxLines="1"
                 android:text="@string/quick_settings_bluetooth_audio_sharing_button"
+                android:drawableStart="@drawable/ic_bt_le_audio_sharing_18dp"
+                android:drawablePadding="10dp"
+                android:drawableTint="?android:attr/textColorPrimary"
+                app:layout_constrainedWidth="true"
+                app:layout_constraintHorizontal_bias="0"
+                app:layout_constraintEnd_toStartOf="@+id/done_button"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintTop_toBottomOf="@+id/barrier"
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 4234fca5..dcd3fa6 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -44,5 +44,5 @@
         app:layout_constraintBottom_toBottomOf="parent"
         />
 
-    <include layout="@layout/dream_overlay_status_bar_view" />
+    <include layout="@layout/ambient_status_bar_view" />
 </com.android.systemui.dreams.DreamOverlayContainerView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml
index 1d9307b..17c0222 100644
--- a/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_preset_dropdown_item.xml
@@ -22,4 +22,5 @@
     android:minHeight="@dimen/hearing_devices_preset_spinner_height"
     android:paddingStart="@dimen/hearing_devices_preset_spinner_text_padding_start"
     android:gravity="center_vertical"
+    android:textDirection="locale"
     android:ellipsize="end" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml
index 77172ca..d512e7c 100644
--- a/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_preset_spinner_selected.xml
@@ -32,6 +32,7 @@
         android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
         android:textSize="14sp"
         android:gravity="center_vertical"
+        android:textDirection="locale"
         android:layout_weight="1" />
     <TextView
         android:id="@+id/hearing_devices_preset_option_text"
@@ -42,5 +43,6 @@
         android:gravity="center_vertical"
         android:ellipsize="end"
         android:maxLines="1"
+        android:textDirection="locale"
         android:layout_weight="1" />
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/immersive_mode_cling.xml b/packages/SystemUI/res/layout/immersive_mode_cling.xml
index e6529b9..20b7cd3 100644
--- a/packages/SystemUI/res/layout/immersive_mode_cling.xml
+++ b/packages/SystemUI/res/layout/immersive_mode_cling.xml
@@ -14,78 +14,67 @@
      limitations under the License.
 -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:theme="@android:style/Theme.DeviceDefault.DayNight"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/immersive_cling_bg"
+    android:gravity="center_vertical"
+    android:padding="24dp">
+
+    <!-- The top margin of this icon can be adjusted to push the content down to prevent overlapping
+         with the display cutout. -->
+    <ImageView
+        android:id="@+id/immersive_cling_icon"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_centerHorizontal="true"
+        android:scaleType="fitXY"
+        android:src="@drawable/ic_swipe_down"
+        android:tint="?android:attr/colorAccent"
+        android:tintMode="src_in" />
+
+    <TextView
+        android:id="@+id/immersive_cling_title"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:gravity="center_vertical"
-        android:paddingBottom="24dp">
-
-    <FrameLayout
-            android:id="@+id/immersive_cling_chevron"
-            android:layout_width="76dp"
-            android:layout_height="76dp"
-            android:layout_marginTop="-24dp"
-            android:layout_centerHorizontal="true">
-
-        <ImageView
-                android:id="@+id/immersive_cling_back_bg_light"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:scaleType="center"
-                android:src="@drawable/immersive_cling_light_bg_circ" />
-
-        <ImageView
-                android:id="@+id/immersive_cling_back_bg"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:scaleType="center"
-                android:src="@drawable/immersive_cling_bg_circ" />
-
-        <ImageView
-                android:id="@+id/immersive_cling_ic_expand_more"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:paddingTop="8dp"
-                android:scaleType="center"
-                android:src="@drawable/ic_expand_more_48dp"/>
-    </FrameLayout>
+        android:layout_below="@id/immersive_cling_icon"
+        android:layout_marginTop="20dp"
+        android:gravity="center_horizontal"
+        android:text="@string/immersive_cling_title"
+        android:textColor="?androidprv:attr/materialColorOnSurface"
+        android:textSize="24sp"
+        android:fontFamily="google-sans" />
 
     <TextView
-            android:id="@+id/immersive_cling_title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/immersive_cling_chevron"
-            android:paddingEnd="48dp"
-            android:paddingStart="48dp"
-            android:paddingTop="40dp"
-            android:text="@string/immersive_cling_title"
-            android:textColor="?android:attr/textColorPrimaryInverse"
-            android:textSize="24sp" />
-
-    <TextView
-            android:id="@+id/immersive_cling_description"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_below="@id/immersive_cling_title"
-            android:paddingEnd="48dp"
-            android:paddingStart="48dp"
-            android:paddingTop="12.6dp"
-            android:text="@string/immersive_cling_description"
-            android:textColor="?android:attr/textColorPrimaryInverse"
-            android:textSize="16sp" />
+        android:id="@+id/immersive_cling_description"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/immersive_cling_title"
+        android:paddingTop="14dp"
+        android:gravity="center_horizontal"
+        android:text="@string/immersive_cling_description"
+        android:textColor="?androidprv:attr/materialColorOnSurfaceVariant"
+        android:textSize="14sp"
+        android:fontFamily="google-sans" />
 
     <Button
-            android:id="@+id/ok"
-            style="@android:style/Widget.Material.Button.Borderless"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignParentEnd="true"
-            android:layout_below="@+id/immersive_cling_description"
-            android:layout_marginEnd="40dp"
-            android:layout_marginTop="18dp"
-            android:paddingEnd="8dp"
-            android:paddingStart="8dp"
-            android:text="@string/immersive_cling_positive"
-            android:textColor="?android:attr/textColorPrimaryInverse"
-            android:textSize="14sp" />
-
+        android:id="@+id/ok"
+        style="@android:style/Widget.Material.Button.Borderless.Colored"
+        android:background="@drawable/immersive_cling_btn_bg"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_below="@+id/immersive_cling_description"
+        android:layout_marginTop="24dp"
+        android:paddingStart="18dp"
+        android:paddingEnd="18dp"
+        android:minWidth="48dp"
+        android:minHeight="48dp"
+        android:text="@string/immersive_cling_positive"
+        android:textColor="?androidprv:attr/materialColorOnPrimary"
+        android:textAllCaps="false"
+        android:textSize="14sp"
+        android:textFontWeight="500"
+        android:fontFamily="google-sans" />
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index f3f472b..84ab0f1 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -18,6 +18,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:theme="@style/FloatingOverlay"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 221b791..fbb07be 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -62,7 +62,9 @@
     <com.android.systemui.keyguard.ui.view.KeyguardRootView
         android:id="@id/keyguard_root_view"
         android:layout_width="match_parent"
-        android:layout_height="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 -->
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 6a14c21..c28dc50 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -33,10 +33,13 @@
         android:gravity="center_vertical"
         android:orientation="horizontal"/>
 
+    <!-- PaddingEnd is added to balance hover padding, compensating for paddingStart in statusIcons.
+         See b/339589733 -->
     <com.android.systemui.battery.BatteryMeterView android:id="@+id/battery"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:clipToPadding="false"
         android:clipChildren="false"
+        android:paddingEnd="@dimen/status_bar_battery_end_padding"
         systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />
 </LinearLayout>
diff --git a/packages/SystemUI/res/raw/keep.xml b/packages/SystemUI/res/raw/keep_homecontrols_sq.xml
similarity index 100%
rename from packages/SystemUI/res/raw/keep.xml
rename to packages/SystemUI/res/raw/keep_homecontrols_sq.xml
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index a737ec7..fd768d8 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tik om te bekyk"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Kon nie skermopname stoor nie"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Kon nie skermopname begin nie"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Kwessieopnemer"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Verwerk tans kwessieopname"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Deurlopende kennisgewing vir ’n kwessieversamelingsessie"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Kon nie kwessieopname stoor nie"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Kon nie kwessieopname begin nie"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Bekyk tans volskerm"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Swiep van bo af as jy wil uitgaan."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Het dit"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Terug"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Tuis"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Kenmerke soos Kitsdeel en Kry My Toestel gebruik Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sal môreoggend aanskakel"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Oudiodeling"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deel tans oudio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Watter deel van jou toestelervaring is geraak?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Kies soort kwessie"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skermopname"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Werkverrigting"</item>
-    <item msgid="1627504621139124393">"Gebruikerkoppelvlak"</item>
-    <item msgid="8309220355268900335">"Battery"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Werkverrigting"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Gebruikerkoppelvlak"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termies"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Gehoortoestelle"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktief"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppel"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Gehoortoestelle"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bind nuwe toestel saam"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knoppie <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Oppyl"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Afpyl"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Linkspyl"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Regspyl"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Middel"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Spasie"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tik vir meer inligting"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Geen wekker nie"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"voer skermslot in"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Vingerafdruksensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"staaf"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"gaan by toestel in"</string>
@@ -1201,7 +1228,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string>
-    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan programme en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan apps en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string>
     <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Skakel vliegtuigmodus af"</string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil die volgende teël by Kitsinstellings voeg"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gevou"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"oopgevou"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batterykrag oor"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusbattery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koppel jou stilus aan ’n laaier"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus se battery is amper pap"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Onlangs gebruik deur <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Word gebruik deur <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Onlangs gebruik deur <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Stelsel"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Verrigting van veelvuldige take"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appkortpaaie"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toeganklikheid"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 0d15f6d..9c24d30 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ለመመልከት መታ ያድርጉ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"የማያ ገጽ ቀረጻን ማስቀመጥ ላይ ስህተት"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"የማያ ገፅ ቀረጻን መጀመር ላይ ስህተት"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ችግር መመዝገቢያ"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"የአሰራር ችግር አመዘጋገብ"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ለችግር መሰብሰብ ክፍለ ጊዜ ቀጣይነት ያለው ማሳወቂያ"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"የችግር ምዝገባ ማስቀመጥ ላይ ስህተት"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ችግር ምዝገባ ማስጀመር ላይ ስህተት"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"በሙሉ ገጽ ዕይታ በማየት ላይ"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"ለመውጣት፣ ከላይ ወደታች ያንሸራትቱ።"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"ገባኝ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"ተመለስ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"መነሻ"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ተቀምጧል"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ግንኙነትን አቋርጥ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ያግብሩ"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"እንደ ፈጣን ማጋራት እና የእኔን መሣሪያ አግኝ ያሉ ባህሪዎች ብሉቱዝን ይጠቀማሉ"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ብሉቱዝ ነገ ጠዋት ይበራል"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"የድምጽ ማጋራት"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ድምጽን በማጋራት ላይ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ኦዲዮ አጋራ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ኦዲዮ በማጋራት ላይ"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"የትኛው የመሣሪያዎ ተሞክሮ ክፍል ተጎድቷል?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"የችግሩን አይነት ይምረጡ"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"የማያ መቅረጫ"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"አፈጻጸም"</item>
-    <item msgid="1627504621139124393">"የተጠቃሚ በይነገፅ"</item>
-    <item msgid="8309220355268900335">"ባትሪ"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"አፈጻጸም"</string>
+    <string name="user_interface" msgid="3712869377953950887">"የተጠቃሚ በይነገፅ"</string>
+    <string name="thermal" msgid="6758074791325414831">"ተርማል"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"የአንድ እጅ ሁነታ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"የመስሚያ መሣሪያዎች"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ገቢር"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ግንኙነት ተቋርጧል"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"የመስማት ችሎታ መሣሪያ"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"አዲስ መሣሪያ ያጣምሩ"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"አዲስ መሣሪያ ለማጣመር ጠቅ ያድርጉ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ቅድመ-ቅምጥን ማዘመን አልተቻለም"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ቅድመ-ቅምጥ"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"የቀጥታ ስርጭት መግለጫ ጽሁፍ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"የመሣሪያ ማይክሮፎን እገዳ ይነሳ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"የመሣሪያ ካሜራ እገዳ ይነሳ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"የመሣሪያ ካሜራ እና ማይክሮፎን እገዳ ይነሳ?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"አዝራር <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"መነሻ"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"ተመለስ"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"የላይ ቀስት"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"የታች ቀስት"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"የግራ ቀስት"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"የቀኝ ቀስት"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"መሃል"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"ክፍተት"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"አስገባ"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ለበለጠ መረጃ መታ ያድርጉ"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ምንም ማንቂያ አልተቀናበረም"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ማያ ገጽ መቆለፊያ ያስገቡ"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"የጣት አሻራ ዳሳሽ"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ያረጋግጡ"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"መሣሪያን ያስገቡ"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"የታጠፈ"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"የተዘረጋ"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ባትሪ ይቀራል"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"የብሮስፌ ባትሪ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ብሮስፌዎን ከኃይል መሙያ ጋር ያገናኙ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"የብሮስፌ ባትሪ ዝቅተኛ ነው"</string>
     <string name="video_camera" msgid="7654002575156149298">"የቪድዮ ካሜራ"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"በቅርብ ጊዜ በ<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ጥቅም ላይ ውሏል"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"በ<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ጥቅም ላይ እየዋለ ነው"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"በቅርብ ጊዜ በ<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ጥቅም ላይ ውሏል"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"ሥርዓት"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ብዙ ተግባራትን በተመሳሳይ ጊዜ ማከናወን"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ግብዓት"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"የመተግበሪያ አቋራጮች"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ተደራሽነት"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"የቁልፍ ሰሌዳ አቋራጮች"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 7c998d3..8711e78 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"انقر لعرض التسجيل"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"حدث خطأ أثناء حفظ تسجيل محتوى الشاشة."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"حدث خطأ في بدء تسجيل الشاشة"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"مسجّلة المشاكل"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"يجري معالجة تسجيل المشكلة."</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"إشعار بنشاط مستمر في الخلفية لجلسة جمع البيانات حول المشكلة"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"حدث خطأ أثناء حفظ تسجيل المشكلة."</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"حدث خطأ أثناء بدء تسجيل المشكلة."</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"جارٍ العرض بملء الشاشة"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"للخروج، مرِّر سريعًا من أعلى الشاشة لأسفلها."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"حسنًا"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"رجوع"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"الرئيسية"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"إلغاء الربط"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"تفعيل"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‏يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\""</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"سيتم تفعيل البلوتوث صباح الغد"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"مشاركة الصوت"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"مشاركة الصوت"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string>
@@ -301,11 +331,11 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"المستخدم"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"الإنترنت"</string>
-    <string name="quick_settings_networks_available" msgid="1875138606855420438">"الشبكات متوفرة"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"تتوفّر شبكات"</string>
     <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"الشبكات غير متوفرة"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"‏لا تتوفر أي شبكة Wi-Fi"</string>
     <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"جارٍ التفعيل…"</string>
-    <string name="quick_settings_cast_title" msgid="3033553249449938182">"الإرسال"</string>
+    <string name="quick_settings_cast_title" msgid="3033553249449938182">"البث"</string>
     <string name="quick_settings_casting" msgid="1435880708719268055">"جارٍ الإرسال"</string>
     <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"جهاز لا يحمل اسمًا"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"لا يتوفر أي جهاز"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ما هو الجانب الذي تأثّر في تجربة استخدام الجهاز؟"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"اختيار نوع المشكلة"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"تسجيل الشاشة"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"الأداء"</item>
-    <item msgid="1627504621139124393">"واجهة المستخدم"</item>
-    <item msgid="8309220355268900335">"البطارية"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"الأداء"</string>
+    <string name="user_interface" msgid="3712869377953950887">"واجهة المستخدم"</string>
+    <string name="thermal" msgid="6758074791325414831">"الأداء الحراري"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعات الأذن الطبية"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"متّصلة حاليًا"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غير متّصلة"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعات الأذن الطبية"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"إقران جهاز جديد"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"انقر لإقران جهاز جديد"</string>
@@ -615,7 +645,7 @@
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"‏%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string>
     <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"‏%1$s. انقر للتعيين على الاهتزاز."</string>
     <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"‏%1$s. انقر لكتم الصوت."</string>
-    <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"التحكُّم في مستوى الضجيج"</string>
+    <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"التحكُّم في مستوى الضوضاء"</string>
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"الصوت المكاني"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"غير مفعّل"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"تفعيل"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"الزر <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"سهم متّجه للأعلى"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"سهم متّجه للأسفل"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"سهم متّجه لليسار"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"سهم متّجه لليمين"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"وسط"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"مسافة"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"انقر للحصول على مزيد من المعلومات."</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"لم يتم ضبط منبّه"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"إدخال الرمز لفتح القفل"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"مستشعر بصمات الإصبع"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"المصادقة"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"الدخول إلى الجهاز"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"مطوي"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"غير مطوي"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"‫%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"النسبة المئوية المتبقية من شحن البطارية: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"مستوى شحن بطارية قلم الشاشة: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"عليك توصيل قلم الشاشة بشاحن."</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"بطارية قلم الشاشة منخفضة"</string>
     <string name="video_camera" msgid="7654002575156149298">"كاميرا فيديو"</string>
@@ -1318,26 +1345,26 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"تم الاستخدام مؤخرًا في <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"قيد الاستخدام في <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"تم الاستخدام مؤخرًا في <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"النظام"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"تعدُّد المهام"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"الإدخال"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"اختصارات التطبيقات"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"تسهيل الاستخدام"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"اختصارات لوحة المفاتيح"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"اختصارات طلبات البحث"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏مستوى الإضاءة: %1$d من %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"إدارة المنزل آليًّا بشكل سريع من شاشة الاستراحة"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"يمكنك إدارة المنزل آليًّا بشكل سريع من شاشة الاستراحة"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index a6ec1a0..785f3f4 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"চাবলৈ টিপক"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ৰেকৰ্ড কৰা স্ক্ৰীন ছেভ কৰোঁতে আসোঁৱাহ হৈছে"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"স্ক্রীন ৰেকৰ্ড কৰা আৰম্ভ কৰোঁতে আসোঁৱাহ হৈছে"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"সমস্যা ৰেকৰ্ডাৰ"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"সমস্যা ৰেকৰ্ড কৰি থকা হৈছে"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"সমস্যা সংগ্ৰহ কৰা এটা ছেশ্বনৰ বাবে চলিত জাননী"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"সমস্যাৰ ৰেকৰ্ডিং ছেভ কৰাত আসোঁৱাহ"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"সমস্যাৰ ৰেকৰ্ডিং আৰম্ভ কৰোঁতে আসোঁৱাহ হৈছে"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"পূৰ্ণ স্ক্ৰীনত চাই আছে"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"বাহিৰ হ’বলৈ ওপৰৰ পৰা তললৈ ছোৱাইপ কৰক।"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"বুজি পালোঁ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"উভতি যাওক"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"গৃহ পৃষ্ঠাৰ বুটাম"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ছেভ কৰা হৈছে"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"সংযোগ বিচ্ছিন্ন কৰক"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"সক্ৰিয় কৰক"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share আৰু Find My Deviceৰ দৰে সুবিধাসমূহে ব্লুটুথ ব্যৱহাৰ কৰে"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"কাইলৈ পুৱা ব্লুটুথ অন হ’ব"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিঅ’ শ্বেয়াৰ কৰা"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"অডিঅ’ শ্বেয়াৰ কৰক"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"আপোনাৰ ডিভাইচৰ অভিজ্ঞতাৰ কোনটো অংশ প্ৰভাৱিত হৈছিল?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যাৰ প্ৰকাৰ বাছনি কৰক"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্ৰীন ৰেকৰ্ড"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"কাৰ্যদক্ষতা"</item>
-    <item msgid="1627504621139124393">"ব্যৱহাৰকাৰীৰ ইণ্টাৰফে’চ"</item>
-    <item msgid="8309220355268900335">"বেটাৰী"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"পাৰদৰ্শিতা"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ব্যৱহাৰকাৰীৰ ইণ্টাৰফে’চ"</string>
+    <string name="thermal" msgid="6758074791325414831">"থাৰ্মেল"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"শুনাৰ ডিভাইচ"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"সক্ৰিয় হৈ আছে"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"সংযোগ বিচ্ছিন্ন কৰা হ’ল"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"শুনাৰ ডিভাইচ"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইচ পেয়াৰ কৰিবলৈ ক্লিক কৰক"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বুটাম"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"হ\'ম"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"উভতি যাওক"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ওপৰলৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"তললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"বাওঁফাললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"সোঁফাললৈ নিৰ্দেশ কৰা কাঁড় চিহ্ন"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"স্ক্ৰীনৰ মাজত"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"স্পেচ"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"এণ্টাৰ"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"অধিক তথ্যৰ বাবে টিপক"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"কোনো এলাৰ্ম ছেট কৰা হোৱা নাই"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"স্ক্ৰীন লকটো দিয়ক"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰ"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"বিশ্বাসযোগ্যতা প্ৰমাণ কৰক"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইচ আনলক কৰক"</string>
@@ -1268,14 +1293,14 @@
     <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"এতিয়াই স্ক্ৰীন সলনি কৰক"</string>
     <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"ফ’নটো আনফ’ল্ড কৰক"</string>
     <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"স্ক্ৰীন সলনি কৰিবনে?"</string>
-    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউছনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string>
-    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউছনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string>
+    <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউশ্বনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string>
+    <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউশ্বনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string>
     <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"জপাব পৰা ডিভাইচৰ জাপ খুলি থকা হৈছে"</string>
     <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"জপাব পৰা ডিভাইচৰ ওলোটাই থকা হৈছে"</string>
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফ’ল্ড কৰা"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"আনফ’ল্ড কৰা"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> বেটাৰী বাকী আছে"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ষ্টাইলাছৰ বেটাৰী <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"আপোনাৰ ষ্টাইলাছ এটা চাৰ্জাৰৰ সৈতে সংযোগ কৰক"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ষ্টাইলাছৰ বেটাৰী কম আছে"</string>
     <string name="video_camera" msgid="7654002575156149298">"ভিডিঅ’ কেমেৰা"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"শেহতীয়াকৈ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)এ ব্যৱহাৰ কৰিছে"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)এ ব্যৱহাৰ কৰি আছে"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"শেহতীয়াকৈ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)এ ব্যৱহাৰ কৰিছে"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"ছিষ্টেম"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"এপ্ শ্বৰ্টকাটসমূহ"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"সাধ্য সুবিধা"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"কীব’ৰ্ডৰ শ্বৰ্টকাট"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index da20087..1c75a9b3 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Baxmaq üçün toxunun"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Ekran çəkimini yadda saxlayarkən xəta oldu"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ekranın yazılması ilə bağlı xəta"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Problem qeydə alan"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Problem qeydi emal edilir"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Problemin əldə edilməsi sessiyası üçün davam edən bildiriş"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Problem qeydini yadda saxlayarkən xəta"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Problemi qeydə almağa başlayarkən xəta"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Tam ekran rejimi"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Çıxmaq üçün yuxarıdan aşağı sürüşdürün."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Anladım"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Geri"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Ana səhifə"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"əlaqəni kəsin"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivləşdirin"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Cəld Paylaşım və Cihazın Tapılması kimi funksiyalar Bluetooth istifadə edir"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sabah səhər aktiv ediləcək"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio paylaşma"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio paylaşılır"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz istifadəsinə necə təsir etdi?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problem növü seçin"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran qeydəalma"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performans"</item>
-    <item msgid="1627504621139124393">"İstifadəçi interfeysi"</item>
-    <item msgid="8309220355268900335">"Batareya"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performans"</string>
+    <string name="user_interface" msgid="3712869377953950887">"İstifadəçi interfeysi"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Birəlli rejim"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eşitmə cihazları"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlantı kəsildi"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eşitmə cihazları"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz birləşdirin"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncəllənmədi"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Avtomatik subtitrlər"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonu blokdan çıxarılsın?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerası blokdan çıxarılsın?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası və mikrofonu blokdan çıxarılsın?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Düymə <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Əsas səhifə"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Geri"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Yuxarı ox"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Aşağı ox"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Sol ox"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Sağ ox"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Mərkəz"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Boşluq"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Daxil olun"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ətraflı məlumat üçün toxunun"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Siqnal ayarlanmayıb"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ekran kilidi daxil edin"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Barmaq izi sensoru"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"doğrulayın"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz daxil edin"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"qatlanmış"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"açıq"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> enerji qalıb"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusun enerjisi: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Qələmi adapterə qoşun"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Qələm enerjisi azdır"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Bu yaxınlarda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) istifadə edib"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) istifadə edir"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Bu yaxınlarda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) istifadə edib"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoxsaylı tapşırıq icrası"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Daxiletmə"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tətbiq qısayolları"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Xüsusi imkanlar"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index fafe718..6072605 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Dodirnite da biste pregledali"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Greška pri čuvanju snimka ekrana"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Greška pri pokretanju snimanja ekrana"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Snimač problema"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Obrađuje se snimak problema"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"obaveštenje o aktivnosti u toku za sesiju prikupljanja podataka o problemu"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Greška pri čuvanju snimka problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Greška pri pokretanju snimanja problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Prikazuje se ceo ekran"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Da biste izašli, prevucite nadole odozgo."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Važi"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Nazad"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Početna"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinite vezu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivirajte"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutru"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvuka"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deli se zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deli zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deli se zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji deo doživljaja na uređaju je ovo uticalo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izaberite tip problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Učinak"</item>
-    <item msgid="1627504621139124393">"Korisnički interfejs"</item>
-    <item msgid="8309220355268900335">"Baterija"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performanse"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termalna kamera"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednom rukom"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Upari novi uređaj"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Taster Početna"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Taster Nazad"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica nagore"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica nadole"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica nalevo"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica nadesno"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Taster sa centralnom strelicom"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Razmak"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nije podešen"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"unesite otključavanje ekrana"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor za otisak prsta"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"potvrdite identitet"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"unesite uređaj"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo je još<xliff:g id="PERCENTAGE">%s</xliff:g> baterije"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija pisaljke je na <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisaljku sa punjačem"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Nizak nivo baterije pisaljke"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string>
@@ -1327,6 +1352,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</string>
     <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 1455a9c..df1227f 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Націсніце для прагляду"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Памылка захавання запісу экрана"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Памылка пачатку запісу экрана"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Запіс праблемы"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Ідзе апрацоўка запісу праблемы"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Бягучае апавяшчэнне пра сеанс збору даных аб праблеме"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Пры захаванні запісу праблемы адбылася памылка"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Пры спробе пачаць запіс праблемы адбылася памылка"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Прагляд у поўнаэкранным рэжыме"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Каб выйсці, правядзіце пальцам па экране зверху ўніз."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Зразумела"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Назад"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"На Галоўную старонку"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Захавана"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"адключыць"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"актываваць"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аўтаматычнае ўключэнне заўтра"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth выкарыстоўваецца такімі функцыямі і сэрвісамі, як Хуткае абагульванне і Знайсці прыладу"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth уключыцца заўтра раніцай"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Абагульванне аўдыя"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ідзе абагульванне аўдыя"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Абагуліць аўдыя"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Ідзе абагульванне аўдыя"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -355,19 +383,19 @@
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Пачаць"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Спыніць"</string>
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Запіс праблемы"</string>
-    <string name="qs_record_issue_start" msgid="2979831312582567056">"Пачынайце"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Пачаць"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Спыніцеся"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Справаздача"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"З чым была звязана праблема, якая вам сустрэлася?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберыце тып праблемы"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запіс экрана"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Прадукцыйнасць"</item>
-    <item msgid="1627504621139124393">"Карыстальніцкі інтэрфейс"</item>
-    <item msgid="8309220355268900335">"Акумулятар"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Прадукцыйнасць"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Карыстальніцкі інтэрфейс"</string>
+    <string name="thermal" msgid="6758074791325414831">"Тэрмальныя паказчыкі"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Рэжым кіравання адной рукой"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слыхавыя апараты"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Актыўныя"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Адключаны"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слыхавыя апараты"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спалучыць новую прыладу"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Націсніце, каб спалучыць новую прыладу"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрэлка ўверх"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрэлка ўніз"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрэлка ўлева"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрэлка ўправа"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Цэнтр"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Прабел"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Націсніце, каб убачыць больш"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Няма будзільнікаў"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"прыступіць да разблакіроўкі экрана"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер адбіткаў пальцаў"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"правесці аўтэнтыфікацыю"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"адкрыць галоўны экран прылады"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складзена"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"раскладзена"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Засталося зараду: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Зарад акумулятара пяра – <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Падключыце пяро да зараднай прылады"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Нізкі ўзровень зараду пяра"</string>
     <string name="video_camera" msgid="7654002575156149298">"Відэакамера"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Нядаўна выкарыстоўваўся праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Зараз выкарыстоўваецца праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Нядаўна выкарыстоўваўся праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Сістэма"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Шматзадачнасць"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Увод"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ярлыкі праграм"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Спецыяльныя магчымасці"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Спалучэнні клавіш"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index a59ca852..b7c8467 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Докоснете за преглед"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Грешка при запазването на записа на екрана"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"При стартирането на записа на екрана възникна грешка"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Записване на проблем"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Записът се обработва"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Текущо известие за сесия за събиране на данни за проблем"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Грешка при запазването на записа на проблема"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Грешка при стартирането на записа на проблема"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Изглед на цял екран"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"За изход прекарайте пръст надолу от горната част."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Разбрах"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Назад"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Начало"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Запазено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекратяване на връзката"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активиране"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функции като „Бързо споделяне“ и „Намиране на устройството ми“ използват Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ще се включи утре сутрин"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделяне на звука"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Звукът се споделя"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С какво имахте проблем?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис на екрана"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Ефективност"</item>
-    <item msgid="1627504621139124393">"Потребителски интерфейс"</item>
-    <item msgid="8309220355268900335">"Батерия"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Ефективност"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string>
+    <string name="thermal" msgid="6758074791325414831">"Термално"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухови апарати"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Няма връзка"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухови апарати"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Сдвояване на ново устройство"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за сдвояване на ново устройство"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Бутон „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Начало"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка за нагоре"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка за надолу"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка за наляво"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка за надясно"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Център"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Интервал"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Докоснете за още информация"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Няма зададен будилник"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"въведете опция за заключване на екрана"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отпечатъци"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"удостоверяване"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"вход в устройството"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Оставаща батерия: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерия на писалката: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Свържете писалката към зарядно устройство"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Батерията на писалката е изтощена"</string>
     <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Наскоро използвано от <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Използва се от <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Наскоро използвано от <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Системни"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Изпълняване на няколко задачи едновременно"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Въвеждане"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Преки пътища към приложения"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Достъпност"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Клавишни комбинации"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка на клавиатурата"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d от %2$d"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 72e9d51..9fa4c6e 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"দেখতে ট্যাপ করুন"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"স্ক্রিন রেকর্ডিং সেভ করার সময় কোনও সমস্যা হয়েছে"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"স্ক্রিন রেকর্ডিং শুরু করার সময় সমস্যা হয়েছে"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Recorder-এ সমস্যা হয়েছে"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"সমস্যা সংক্রান্ত রেকর্ডিং প্রসেস করা হচ্ছে"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"সমস্যা সংগ্রহ সেশনের জন্য অনগোইং নোটিফিকেশন"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"সমস্যা সংক্রান্ত রেকর্ডিং সেভ করার সময় সমস্যা হয়েছে"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"সমস্যা সংক্রান্ত রেকর্ডিং শুরু করতে সমস্যা হয়েছে"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"ফুল-স্ক্রিনে দেখা হচ্ছে"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"বেরিয়ে যেতে উপর থেকে নিচের দিকে সোয়াইপ করুন।"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"বুঝেছি"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"ফিরুন"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"হোম"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ডিসকানেক্ট করুন"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"চালু করুন"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল আবার অটোমেটিক চালু হবে"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"দ্রুত শেয়ার ও Find My Device-এর মতো ফিচার ব্লুটুথ ব্যবহার করে"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিও শেয়ারিং"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিও শেয়ার করা হচ্ছে"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ডিভাইস ব্যবহার করার সময় কোথায় অসুবিধা হয়েছিল?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যার প্রকার বেছে নিন"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্রিন রেকর্ড"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"পারফর্ম্যান্স"</item>
-    <item msgid="1627504621139124393">"ইউজার ইন্টারফেস"</item>
-    <item msgid="8309220355268900335">"ব্যাটারি"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"পারফর্ম্যান্স"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ইউজার ইন্টারফেস"</string>
+    <string name="thermal" msgid="6758074791325414831">"থার্মাল"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এক হাতে ব্যবহার করার মোড"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"হিয়ারিং ডিভাইস"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"অ্যাক্টিভ"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ডিসকানেক্ট হয়ে গেছে"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"হিয়ারিং ডিভাইস"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইস পেয়ার করুন"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইস পেয়ার করতে ক্লিক করুন"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্রিসেট আপডেট করা যায়নি"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্রিসেট"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"লাইভ ক্যাপশন"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইসের মাইক্রোফোন আনব্লক করতে চান?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইসের ক্যামেরা আনব্লক করতে চান?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইসের ক্যামেরা এবং মাইক্রোফোন আনব্লক করতে চান?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> বোতাম"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"হোম"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"ফিরুন"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ঊর্ধমুখী তীরচিহ্ন"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"নিম্নমুখী তীরচিহ্ন"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"বাঁদিকের তীরচিহ্ন"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ডানদিকের তীরচিহ্ন"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"কেন্দ্র"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"এন্টার"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"আরও তথ্যের জন্য ট্যাপ করুন"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"কোনও অ্যালার্ম সেট করা নেই"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"স্ক্রিন লক খুলুন"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ফিঙ্গারপ্রিন্ট সেন্সর"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"যাচাই করিয়ে নিন"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইস আনলক করুন"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফোল্ড করা রয়েছে"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ফোল্ড করা নেই"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ব্যাটারির চার্জ বাকি আছে"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"স্টাইলাস ব্যাটারি <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"কোনও চার্জারের সাথে আপনার স্টাইলাস কানেক্ট করুন"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"স্টাইলাস ব্যাটারিতে চার্জ কম আছে"</string>
     <string name="video_camera" msgid="7654002575156149298">"ভিডিও ক্যামেরা"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"সম্প্রতি <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ব্যবহার করা হয়েছে"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ব্যবহার করা হচ্ছে"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"সম্প্রতি <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ব্যবহার করা হয়েছে"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"সিস্টেম"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"মাল্টিটাস্কিং"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ইনপুট"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"অ্যাপ শর্টকাট"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"অ্যাক্সেসিবিলিটি"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"কীবোর্ড শর্টকাট"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সার্চ শর্টকাট"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 93c8995..8dee87a 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Dodirnite da vidite"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Greška prilikom pohranjivanja snimka ekrana"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Greška pri pokretanju snimanja ekrana"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Snimač problema"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Obrada snimka problema"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Obavještenje o aktivnosti u pozadini za sesiju prikupljanja podataka o problemu"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Došlo je do greške prilikom pohranjivanja snimka problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Došlo je do greške prilikom pokretanja snimanja problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Prikazivanje preko cijelog ekrana"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Da izađete, prevucite s vrha nadolje."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Razumijem"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Nazad"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Dugme za početnu stranicu"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekid veze"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Dijeljenje zvuka"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Dijeljenje zvuka"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dijeli zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zajedničko slušanje"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performanse"</item>
-    <item msgid="1627504621139124393">"Korisnički interfejs"</item>
-    <item msgid="8309220355268900335">"Baterija"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performanse"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termalno"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadane postavke nije uspjelo"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Zadana postavka"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Automatski titlovi"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokirati mikrofon uređaja?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokirati kameru uređaja?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokirati kameru i mikrofon uređaja?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Dugme <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Tipka za početak"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Nazad"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica nagore"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica nadolje"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica ulijevo"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica udesno"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Sredina"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Tipka za razmak"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Tipka za novi red"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nema nijednog alarma"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"unos zaključavanja ekrana"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor za otisak prsta"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentificiranje"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"pristup uređaju"</string>
@@ -1202,7 +1226,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string>
-    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u Postavkama traženja WiFi mreže. "<annotation id="link">"Promijeni"</annotation></string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u postavkama traženja WiFi-ja. "<annotation id="link">"Promijeni"</annotation></string>
     <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Isključi način rada u avionu"</string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću karticu u Brze postavke"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string>
@@ -1211,7 +1235,7 @@
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna}one{# aplikacija je aktivna}few{# aplikacije su aktivne}other{# aplikacija je aktivno}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string>
-    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali može uticati i na vijek trajanja baterije."</string>
+    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali to može uticati i na vijek trajanja baterije."</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zaustavljeno"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Gotovo"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sklopljeno"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otklopljeno"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo baterije: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija pisaljke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Baterija pisaljke je slaba"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string>
@@ -1319,26 +1343,26 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno je koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Koristi aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno je koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice aplikacije"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo pristupajte kontrolama za dom kao čuvaru ekrana"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo pristupajte kontrolama za dom putem čuvara ekrana"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 8ad4b92..f9d343f 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Toca per veure-la"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"S\'ha produït un error en desar la gravació de la pantalla"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"S\'ha produït un error en iniciar la gravació de pantalla"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Gravadora de problemes"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processant gravació del problema"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificació en curs d\'una sessió de recollida de dades del problema"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"S\'ha produït un error en desar la gravació del problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"S\'ha produït un error en iniciar la gravació del problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visualització en pantalla completa"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Per sortir, llisca cap avall des de la part superior."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Entesos"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Enrere"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Inici"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Desat"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconnecta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activa"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Torna\'l a activar automàticament demà"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les funcions com Quick Share i Troba el meu dispositiu utilitzen el Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth s\'activarà demà al matí"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartició d\'àudio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"S\'està compartint l\'àudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Comparteix l\'àudio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"S\'està compartint l\'àudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Rendiment"</item>
-    <item msgid="1627504621139124393">"Interfície d\'usuari"</item>
-    <item msgid="8309220355268900335">"Bateria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Rendiment"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interfície d\'usuari"</string>
+    <string name="thermal" msgid="6758074791325414831">"Tèrmic"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audiòfons"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actiu"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconnectat"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audiòfons"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincula un dispositiu nou"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"No s\'ha pogut actualitzar el valor predefinit"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Valors predefinits"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Subtítols instantanis"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vols desbloquejar el micròfon del dispositiu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vols desbloquejar la càmera del dispositiu?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vols desbloquejar la càmera i el micròfon del dispositiu?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botó <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Inici"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Enrere"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Fletxa amunt"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Fletxa avall"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Fletxa esquerra"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Fletxa dreta"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Espai"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Retorn"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca per obtenir més informació"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Cap alarma definida"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"utilitza el bloqueig de pantalla"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor d\'empremtes digitals"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"accedir al dispositiu"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegat"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegat"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Queda un <xliff:g id="PERCENTAGE">%s</xliff:g> de bateria"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria del llapis òptic: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connecta el llapis òptic a un carregador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria del llapis òptic baixa"</string>
     <string name="video_camera" msgid="7654002575156149298">"Càmera de vídeo"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Utilitzat recentment per <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En ús per <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Utilitzat recentment per <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasca"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Dreceres d\'aplicacions"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilitat"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 1474458..3caa23d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Klepnutím nahrávku zobrazíte"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Při ukládání záznamu obrazovky došlo k chybě"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Při spouštění nahrávání obrazovky došlo k chybě"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Rekordér problémů"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Zpracování záznamu problému"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Upozornění na probíhající shromažďování dat o problému"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Při ukládání záznamu problému došlo k chybě"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Při zahájení záznamu problému došlo k chybě"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Zobrazování přes celou obrazovku"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Režim ukončíte přejetím prstem shora dolů."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Rozumím"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Zpět"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Domů"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojit"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovat"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth využívají funkce jako Quick Share a Najdi moje zařízení."</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno."</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Sdílení zvuku"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sdílení zvuku"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Výkon"</item>
-    <item msgid="1627504621139124393">"Uživatelské rozhraní"</item>
-    <item msgid="8309220355268900335">"Baterie"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Výkon"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termovize"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Naslouchátka"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivní"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojeno"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Naslouchátka"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovat nové zařízení"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tlačítko <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Zpět"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Šipka nahoru"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Šipka dolů"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Šipka vlevo"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Šipka doprava"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Střed"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"TAB"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Mezerník"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -923,7 +948,7 @@
     <string name="mobile_data" msgid="4564407557775397216">"Mobilní data"</string>
     <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
-    <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnuta"</string>
+    <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnutá"</string>
     <string name="bt_is_off" msgid="7436344904889461591">"Bluetooth je vypnuto"</string>
     <string name="dnd_is_off" msgid="3185706903793094463">"Režim Nerušit je vypnut"</string>
     <string name="dnd_is_on" msgid="7009368176361546279">"Režim Nerušit je zapnutý"</string>
@@ -933,7 +958,7 @@
     <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplikace běžící na pozadí"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vypnout mobilní data?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím <xliff:g id="CARRIER">%s</xliff:g> nebudete moci používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím operátora <xliff:g id="CARRIER">%s</xliff:g> nebudete moct používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vašeho operátora"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Přepnout zpět na operátora <xliff:g id="CARRIER">%s</xliff:g>?"</string>
     <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilní data se nebudou automaticky přepínat podle dostupnosti"</string>
@@ -1178,6 +1203,8 @@
     <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="accessibility_bouncer" msgid="5896923685673320070">"zadejte zámek obrazovky"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Snímač otisků prstů"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ověříte"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"zadáte zařízení"</string>
@@ -1211,7 +1238,7 @@
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informace"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivní aplikace"</string>
     <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Tyto aplikace jsou spuštěné a aktivní, i když je nepoužíváte. Zlepšuje to jejich funkčnost, ale může to mít dopad na výdrž baterie."</string>
-    <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Konec"</string>
+    <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zastavit"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zastaveno"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Hotovo"</string>
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Zkopírováno"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"složené"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Zbývá <xliff:g id="PERCENTAGE">%s</xliff:g> baterie"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterie dotykového pera <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Připojte dotykové pero k nabíječce"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Slabá baterie dotykového pera"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedávno použila aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Právě používán aplikací <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedávno použila aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Systém"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Zkratky aplikací"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Přístupnost"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index d10d3e0..5333768 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tryk for at se"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Skærmoptagelsen kunne ikke gemmes"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Skærmoptagelsen kunne ikke startes"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Problemoptagelse"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Behandler optagelse af problem"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Igangværende notifikation om en session med problemindsamling"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Kunne ikke gemme optagelse af problem"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Fejl under forsøg på at starte optagelse af problem"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visning i fuld skærm"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Stryg ned fra toppen for at afslutte."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Tilbage"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Hjem"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"afbryd forbindelse"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivér"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som f.eks. Quick Share og Find min enhed anvender Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveres i morgen tidlig"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Del lyd"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Ydeevne"</item>
-    <item msgid="1627504621139124393">"Brugerflade"</item>
-    <item msgid="8309220355268900335">"Batteri"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Ydeevne"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Brugerflade"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termisk"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivt"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Afbrudt"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Par ny enhed"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Forindstillingen kunne ikke opdateres"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forindstilling"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Livetekstning"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du fjerne blokeringen af enhedens mikrofon?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du fjerne blokeringen af enhedens kamera?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du fjerne blokeringen af enhedens kamera og mikrofon?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>-knap"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Tilbage"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pil op"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pil ned"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Venstrepil"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Højrepil"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midtertast"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Mellemrumstast"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tryk for at få flere oplysninger"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ingen alarm er indstillet"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"angiv skærmlås"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeraftrykssensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"godkende"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"få adgang til enheden"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"foldet"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"foldet ud"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteri tilbage"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteriniveau på styluspen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Slut din styluspen til en oplader"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Lavt batteriniveau på styluspen"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Brugt for nylig af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Bruges af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Brugt for nylig af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Appgenveje"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hjælpefunktioner"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 20707ac..847e25f 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Zum Ansehen tippen"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Fehler beim Speichern der Bildschirmaufzeichnung"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Fehler beim Start der Bildschirmaufzeichnung"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Problem aufzeichnen"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Aufzeichnung wird verarbeitet"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Benachrichtigung über laufende Aufzeichnung eines Problems"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Fehler beim Speichern der Aufzeichnung des Problems"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Fehler beim Starten der Aufzeichnung des Problems"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Vollbildmodus wird aktiviert"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Zum Beenden von oben nach unten wischen."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Ok"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Zurück"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Startbildschirm"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Verknüpfung aufheben"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivieren"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktionen wie „Quick Share“ und „Mein Gerät finden“ verwenden Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wird morgen früh aktiviert"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audiofreigabe"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioinhalte werden freigegeben"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Audioinhalte freigeben"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioinhalte werden freigegeben"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Leistung"</item>
-    <item msgid="1627504621139124393">"Benutzeroberfläche"</item>
-    <item msgid="8309220355268900335">"Akku"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Leistung"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Benutzeroberfläche"</string>
+    <string name="thermal" msgid="6758074791325414831">"Überhitzung"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörgeräte"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nicht verbunden"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörgeräte"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Neues Gerät koppeln"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Voreinstellung konnte nicht aktualisiert werden"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voreinstellung"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Automatische Untertitel"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Blockierung des Gerätemikrofons aufheben?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Blockierung der Gerätekamera aufheben?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blockierung von Gerätekamera und Gerätemikrofon aufheben?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Zurück"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Aufwärtspfeil"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Abwärtspfeil"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Linkspfeil"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Rechtspfeil"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Zentrieren"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulatortaste"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Leertaste"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Eingabetaste"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Für weitere Informationen tippen"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Kein Wecker gestellt"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"Displaysperre eingeben"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerabdrucksensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authentifizieren"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"Eingeben des Geräts"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zugeklappt"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aufgeklappt"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akku bei <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Akkustand des Eingabestifts: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Schließe deinen Eingabestift an ein Ladegerät an"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kürzlich von <xliff:g id="APP_NAME">%1$s</xliff:g> verwendet (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Verwendet von <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kürzlich von <xliff:g id="APP_NAME">%1$s</xliff:g> verwendet (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Eingabe"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-Verknüpfungen"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Bedienungshilfen"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 403ca4d..80fe3d2 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Πατήστε για προβολή"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Σφάλμα κατά την αποθήκευση της εγγραφής οθόνης"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Σφάλμα κατά την έναρξη της εγγραφής οθόνης"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Εργαλείο καταγραφής προβλημάτων"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Επεξερ. καταγραφής προβλήματος"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ειδοποίηση σε εξέλιξη για μια περίοδο λειτουργίας συλλογής προβλημάτων"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Σφάλμα κατά την αποθήκευση της καταγραφής του προβλήματος"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Σφάλμα κατά την έναρξη της καταγραφής του προβλήματος"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Προβολή σε πλήρη οθόνη"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Για έξοδο, σύρετε προς τα κάτω από το επάνω μέρος."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Το κατάλαβα"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Πίσω"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Αρχική οθόνη"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Αποθηκεύτηκε"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"αποσύνδεση"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ενεργοποίηση"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Λειτουργίες όπως το Quick Share και η Εύρεση συσκευής χρησιμοποιούν το Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Το Bluetooth θα ενεργοποιηθεί αύριο το πρωί"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Κοινή χρήση ήχου"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Κοινή χρήση ήχου"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ποιο κομμάτι της εμπειρίας συσκευής επηρεάστηκε;"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Επιλογή τύπου προβλήματος"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Εγγραφή οθόνης"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Απόδοση"</item>
-    <item msgid="1627504621139124393">"Διεπαφή χρήστη"</item>
-    <item msgid="8309220355268900335">"Μπαταρία"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Απόδοση"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Διεπαφή χρήστη"</string>
+    <string name="thermal" msgid="6758074791325414831">"Θερμικό"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Συσκευές ακοής"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ενεργά"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Αποσυνδεδεμένα"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Συσκευές ακοής"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Σύζευξη νέας συσκευής"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Κάντε κλικ για σύζευξη νέας συσκευής"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Δεν ήταν δυνατή η ενημέρωση της προεπιλογής"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Προεπιλογή"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Ζωντανοί υπότιτλοι"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Κατάργηση αποκλεισμού μικροφώνου συσκευής;"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Κατάργηση αποκλεισμού κάμερας συσκευής;"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Κατάργηση αποκλεισμού κάμερας και μικροφώνου συσκευής;"</string>
@@ -620,7 +649,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Χωρικός ήχος"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Ανενεργός"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Σταθερός"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Παρακ. κίνησ. κεφαλ."</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Παρακ. κίνησ. κεφαλής"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Πατήστε για να αλλάξετε τη λειτουργία ειδοποίησης ήχου"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"σίγαση"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"κατάργηση σίγασης"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Κουμπί <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Πίσω"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Επάνω βέλος"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Κάτω βέλος"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Αριστερό βέλος"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Δεξί βέλος"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Κέντρο"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Πλήκτρο διαστήματος"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Πατήστε για περισσότερες πληροφορίες."</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Δεν ορίστηκε ξυπνητ."</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"εισαγωγή κλειδώματος οθόνης"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Αισθητήρας δακτυλικών αποτυπωμάτων"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"έλεγχος ταυτότητας"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"εισαγωγή συσκευής"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"διπλωμένη"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ξεδιπλωμένη"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Απομένει το <xliff:g id="PERCENTAGE">%s</xliff:g> της μπαταρίας"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Μπαταρία γραφίδας <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Συνδέστε τη γραφίδα σε έναν φορτιστή"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Χαμηλή στάθμη μπαταρίας γραφίδας"</string>
     <string name="video_camera" msgid="7654002575156149298">"Βιντεοκάμερα"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Χρησιμοποιήθηκε πρόσφατα από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Χρησιμοποιείται από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Χρησιμοποιήθηκε πρόσφατα από την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Σύστημα"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Πολυδιεργασία"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Είσοδος"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Συντομεύσεις εφαρμογών"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Προσβασιμότητα"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 83dffc8..498031f 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"To exit, swipe down from the top."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Back"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performance"</item>
-    <item msgid="1627504621139124393">"User interface"</item>
-    <item msgid="8309220355268900335">"Battery"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performance"</string>
+    <string name="user_interface" msgid="3712869377953950887">"User interface"</string>
+    <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index b1cdcb1..8975cbf 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -125,6 +125,19 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="2685522129492260887">"Stop recording screen?"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="1926783607059442889">"You will stop recording your screen"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"You will stop recording &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Stop recording"</string>
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Stop sharing screen?"</string>
+    <string name="share_to_app_stop_dialog_message" msgid="3181723638915877339">"You will stop sharing your screen"</string>
+    <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"You will stop sharing &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Stop sharing"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="1910372600290258193">"Stop casting screen?"</string>
+    <string name="cast_to_other_device_stop_dialog_message" msgid="1502520537030715412">"You will stop casting your screen"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"You will stop casting &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;"</string>
+    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Stop casting"</string>
+    <string name="close_dialog_button" msgid="4749497706540104133">"Close"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
@@ -135,7 +148,7 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"To exit, swipe down from the top."</string>
+    <string name="immersive_cling_description" msgid="2717426731830851921">"To exit, swipe down from the top of your screen"</string>
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Back"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
@@ -278,11 +291,11 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+    <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"Automatically turn on tomorrow"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,13 +374,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performance"</item>
-    <item msgid="1627504621139124393">"User Interface"</item>
-    <item msgid="8309220355268900335">"Battery"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performance"</string>
+    <string name="user_interface" msgid="3712869377953950887">"User Interface"</string>
+    <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
@@ -727,11 +740,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1186,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"authenticate"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1275,7 +1285,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
@@ -1327,6 +1337,11 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
     <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="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
+    <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
+    <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 83dffc8..498031f 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"To exit, swipe down from the top."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Back"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performance"</item>
-    <item msgid="1627504621139124393">"User interface"</item>
-    <item msgid="8309220355268900335">"Battery"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performance"</string>
+    <string name="user_interface" msgid="3712869377953950887">"User interface"</string>
+    <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 83dffc8..498031f 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tap to view"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Error saving screen recording"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Issue Recorder"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processing issue recording"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongoing notification for an issue collection session"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Error saving issue recording"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Error starting issue recording"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Viewing full screen"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"To exit, swipe down from the top."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Got it"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Back"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performance"</item>
-    <item msgid="1627504621139124393">"User interface"</item>
-    <item msgid="8309220355268900335">"Battery"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performance"</string>
+    <string name="user_interface" msgid="3712869377953950887">"User interface"</string>
+    <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Up arrow"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Down arrow"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Left arrow"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Right arrow"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"folded"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"unfolded"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> battery remaining"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylus battery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connect your stylus to a charger"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus battery low"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In use by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recently used by <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App shortcuts"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index bfaccd0..179b792 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -125,6 +125,19 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‎Tap to view‎‏‎‎‏‎"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‏‏‏‎Error saving screen recording‎‏‎‎‏‎"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎Error starting screen recording‎‏‎‎‏‎"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="2685522129492260887">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎Stop recording screen?‎‏‎‎‏‎"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="1926783607059442889">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‏‎You will stop recording your screen‎‏‎‎‏‎"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5285148796772616326">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‎‎‏‏‎‎You will stop recording &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;‎‏‎‎‏‎"</string>
+    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎Stop recording‎‏‎‎‏‎"</string>
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‎‎Stop sharing screen?‎‏‎‎‏‎"</string>
+    <string name="share_to_app_stop_dialog_message" msgid="3181723638915877339">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‏‎You will stop sharing your screen‎‏‎‎‏‎"</string>
+    <string name="share_to_app_stop_dialog_message_specific_app" msgid="124371406810544777">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎You will stop sharing &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;‎‏‎‎‏‎"</string>
+    <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‎Stop sharing‎‏‎‎‏‎"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="1910372600290258193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎Stop casting screen?‎‏‎‎‏‎"</string>
+    <string name="cast_to_other_device_stop_dialog_message" msgid="1502520537030715412">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‎‎‎You will stop casting your screen‎‏‎‎‏‎"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="4891536209254041850">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‎‏‎‎You will stop casting &lt;b&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/b&gt;‎‏‎‎‏‎"</string>
+    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎Stop casting‎‏‎‎‏‎"</string>
+    <string name="close_dialog_button" msgid="4749497706540104133">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‎Close‎‏‎‎‏‎"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎Issue Recorder‎‏‎‎‏‎"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‎‎Processing issue recording‎‏‎‎‏‎"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎Ongoing notification for an issue collection session‎‏‎‎‏‎"</string>
@@ -135,7 +148,7 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎Error saving issue recording‎‏‎‎‏‎"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‏‎‎Error starting issue recording‎‏‎‎‏‎"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎Viewing full screen‎‏‎‎‏‎"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎To exit, swipe down from the top.‎‏‎‎‏‎"</string>
+    <string name="immersive_cling_description" msgid="2717426731830851921">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎To exit, swipe down from the top of your screen‎‏‎‎‏‎"</string>
     <string name="immersive_cling_positive" msgid="3076681691468978568">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎Got it‎‏‎‎‏‎"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‎Back‎‏‎‎‏‎"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎Home‎‏‎‎‏‎"</string>
@@ -278,11 +291,11 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‎Saved‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎disconnect‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‎activate‎‏‎‎‏‎"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‎Automatically turn on again tomorrow‎‏‎‎‏‎"</string>
+    <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‎Automatically turn on tomorrow‎‏‎‎‏‎"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎Features like Quick Share and Find My Device use Bluetooth‎‏‎‎‏‎"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎Bluetooth will turn on tomorrow morning‎‏‎‎‏‎"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‎Audio Sharing‎‏‎‎‏‎"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎Sharing Audio‎‏‎‎‏‎"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎Share audio‎‏‎‎‏‎"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‎‎‎‎Sharing audio‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ battery‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎Audio‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎Headset‎‏‎‎‏‎"</string>
@@ -361,13 +374,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎What part of your device experience was affected?‎‏‎‎‏‎"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‎Select issue type‎‏‎‎‏‎"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎Screen record‎‏‎‎‏‎"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‎‎Performance‎‏‎‎‏‎"</item>
-    <item msgid="1627504621139124393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‎User Interface‎‏‎‎‏‎"</item>
-    <item msgid="8309220355268900335">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‎Battery‎‏‎‎‏‎"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎Performance‎‏‎‎‏‎"</string>
+    <string name="user_interface" msgid="3712869377953950887">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎User Interface‎‏‎‎‏‎"</string>
+    <string name="thermal" msgid="6758074791325414831">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎Thermal‎‏‎‎‏‎"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎One-handed mode‎‏‎‎‏‎"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎Hearing devices‎‏‎‎‏‎"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎Active‎‏‎‎‏‎"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎Disconnected‎‏‎‎‏‎"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‎Hearing devices‎‏‎‎‏‎"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‎Pair new device‎‏‎‎‏‎"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎Click to pair new device‎‏‎‎‏‎"</string>
@@ -727,11 +740,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎Button ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎Home‎‏‎‎‏‎"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎Back‎‏‎‎‏‎"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎Up arrow‎‏‎‎‏‎"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎Down arrow‎‏‎‎‏‎"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‎Left arrow‎‏‎‎‏‎"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‎Right arrow‎‏‎‎‏‎"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎Center‎‏‎‎‏‎"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‎‏‏‎‏‎‎Tab‎‏‎‎‏‎"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎Space‎‏‎‎‏‎"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‏‎‏‏‏‏‎‎Enter‎‏‎‎‏‎"</string>
@@ -1178,6 +1186,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎Tap for more information‎‏‎‎‏‎"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎No alarm set‎‏‎‎‏‎"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‎enter screen lock‎‏‎‎‏‎"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‎‎‎Fingerprint sensor‎‏‎‎‏‎"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎authenticate‎‏‎‎‏‎"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎enter device‎‏‎‎‏‎"</string>
@@ -1275,7 +1285,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎folded‎‏‎‎‏‎"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎unfolded‎‏‎‎‏‎"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‎%1$s / %2$s‎‏‎‎‏‎"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ battery remaining‎‏‎‎‏‎"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎Stylus battery ‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎Connect your stylus to a charger‎‏‎‎‏‎"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‎Stylus battery low‎‏‎‎‏‎"</string>
     <string name="video_camera" msgid="7654002575156149298">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎Video camera‎‏‎‎‏‎"</string>
@@ -1327,6 +1337,11 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎Search shortcuts‎‏‎‎‏‎"</string>
     <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="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎Back gesture‎‏‎‎‏‎"</string>
+    <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎Home gesture‎‏‎‎‏‎"</string>
+    <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎Action key‎‏‎‎‏‎"</string>
+    <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎Done‎‏‎‎‏‎"</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎Keyboard backlight‎‏‎‎‏‎"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎Level %1$d of %2$d‎‏‎‎‏‎"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‎Home Controls‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 01d2130..0cad6b7 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Presiona para ver"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Se produjo un error al guardar la grabación de pantalla"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error al iniciar la grabación de pantalla"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Grabadora de errores"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Procesando grabación del error"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificación continua por un error durante la sesión de recopilación de datos"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Se produjo un error al guardar la grabación del problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Se produjo un error al iniciar la grabación del problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visualización en pantalla completa"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Para salir, desliza el dedo hacia abajo desde la parte superior."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Entendido"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Atrás"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Página principal"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activar automáticamente mañana"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana a la mañana"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Uso compartido de audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -358,16 +388,16 @@
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de errores"</string>
-    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se vio afectada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Rendimiento"</item>
-    <item msgid="1627504621139124393">"Interfaz de usuario"</item>
-    <item msgid="8309220355268900335">"Batería"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
+    <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo nuevo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Atrás"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flecha hacia arriba"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flecha hacia abajo"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flecha a la izquierda"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha a la derecha"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Intro"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Presiona para obtener más información"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No establecida"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ingresa el bloqueo pantalla"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de huellas dactilares"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ingresar al dispositivo"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de batería restante"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería de la pluma stylus: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu pluma stylus a un cargador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"La pluma stylus tiene poca batería"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
@@ -1318,26 +1345,26 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Uso reciente en <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Uso reciente en <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Tareas múltiples"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede rápidamente a controles de la casa como prot. de pantalla"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa rápidamente los controles de la casa como protector de pantalla"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 7fd07f0..24638f5 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Toca para verla"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"No se ha podido guardar la grabación de pantalla"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"No se ha podido empezar a grabar la pantalla"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Grabadora de problemas"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Procesando grabación de problema"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificación continua de una sesión de obtención de datos del problema"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"No se ha podido guardar la grabación del problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"No se ha podido iniciar la grabación del problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visualización en pantalla completa"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Para salir, desliza de arriba abajo."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Entendido"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Atrás"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Inicio"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana por la mañana"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartir audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Compartir audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Compartiendo audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Rendimiento"</item>
-    <item msgid="1627504621139124393">"Interfaz de usuario"</item>
-    <item msgid="8309220355268900335">"Batería"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
+    <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audífonos"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Emparejar nuevo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se ha podido actualizar el preajuste"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preajuste"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Subtítulos automáticos"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Desbloquear el micrófono del dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Desbloquear la cámara del dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Atrás"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flecha hacia arriba"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flecha hacia abajo"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flecha izquierda"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flecha derecha"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulador"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Espacio"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Intro"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca la pantalla para consultar más información"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ninguna puesta"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"Poner bloqueo de pantalla"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de huellas digitales"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticarte"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"acceder al dispositivo"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batería restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería del lápiz óptico al <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu lápiz óptico a un cargador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Batería del lápiz óptico baja"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recientemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recientemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarea"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Accesos directos a aplicaciones"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 29f0799..8169e82 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Puudutage kuvamiseks"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Viga ekraanisalvestise salvestamisel"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Viga ekraanikuva salvestamise alustamisel"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Probleemisalvesti"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Probleemisalvestise töötlemine"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Probleemikogumise seansi taustamärguanne"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Viga probleemisalvestise salvestamisel"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Viga probleemi salvestamise alustamisel"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Kuvamine täisekraanil"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Väljumiseks pühkige ülevalt alla."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Selge"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Tagasi"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Kodu"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Sellised funktsioonid nagu Kiirjagamine ja Leia mu seade kasutavad Bluetoothi."</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth lülitub sisse homme hommikul"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Heli jagamine"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Heli jagamine"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Toimivus"</item>
-    <item msgid="1627504621139124393">"Kasutajaliides"</item>
-    <item msgid="8309220355268900335">"Aku"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Jõudlus"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Kasutajaliides"</string>
+    <string name="thermal" msgid="6758074791325414831">"Soojus"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ühekäerežiim"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuuldeseadmed"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ühendatud"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ühendus on katkestatud"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuuldeseadmed"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uue seadme sidumine"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Nupp <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Avakuva"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Tagasi"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Ülesnool"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Allanool"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vasaknool"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Paremnool"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Keskele"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Tühik"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Sisestusklahv"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Puudutage lisateabe saamiseks"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Äratust pole"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"sisesta ekraanilukk"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sõrmejäljeandur"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentimiseks"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"seadmesse sisenemiseks"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kokku volditud"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"lahti volditud"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akutase on <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Elektronpliiatsi aku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ühendage elektronpliiats laadijaga"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokaamera"</string>
@@ -1327,6 +1354,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string>
     <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 5d0d778..e9e8518 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Sakatu ikusteko"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Errore bat gertatu da pantaila-grabaketa gordetzean"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Errore bat gertatu da pantaila grabatzen hastean"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Arazo-grabagailua"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Arazoaren grabaketa prozesatzen"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Arazo bat biltzeko saio baten aktibo dagoen jakinarazpena"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Errore bat gertatu da arazoa grabatzean"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Errore bat gertatu da arazoa grabatzen hastean"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Pantaila osoa ikusgai"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Irteteko, pasatu hatza goitik behera."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Ados"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Atzera"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Hasiera"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deskonektatu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktibatu"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Bilatu nire gailua eta beste eginbide batzuek Bluetootha erabiltzen dute"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bihar goizean aktibatuko da Bluetootha"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audioa partekatzea"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioa partekatzen"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Partekatu audioa"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioa partekatzen"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabaketa"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Errendimendua"</item>
-    <item msgid="1627504621139124393">"Erabiltzaile-interfazea"</item>
-    <item msgid="8309220355268900335">"Bateria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Errendimendua"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Erabiltzaile-interfazea"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termikoa"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Entzumen-gailuak"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deskonektatuta"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Entzumen-gailuak"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parekatu beste gailu bat"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ezin izan da eguneratu aurrezarpena"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Aurrezarpena"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Istanteko azpitituluak"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Gailuaren mikrofonoa desblokeatu nahi duzu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Gailuaren kamera desblokeatu nahi duzu?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Gailuaren kamera eta mikrofonoa desblokeatu nahi dituzu?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> botoia"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Hasiera"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Atzera"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Gora egiteko gezi-tekla"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Behera egiteko gezi-tekla"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Ezkerrera egiteko gezi-tekla"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Eskuinera egiteko gezi-tekla"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Erdiratu"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Zuriunea"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Sartu"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Informazio gehiago lortzeko, sakatu hau"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Alarmarik ez"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"erabili pantailaren blokeoa"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Hatz-marken sentsorea"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifikatu"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"sartu gailuan"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"tolestuta"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tolestu gabe"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateriaren <xliff:g id="PERCENTAGE">%s</xliff:g> geratzen da"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Arkatzaren bateria: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Konektatu arkatza kargagailu batera"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Arkatzak bateria gutxi du"</string>
     <string name="video_camera" msgid="7654002575156149298">"Bideokamera"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) aplikazioak erabili du duela gutxi"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak darabil (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) aplikazioak erabili du duela gutxi"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Zeregin bat baino gehiago aldi berean exekutatzea"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Sarrera"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Aplikazioetarako lasterbideak"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index f856a7e..3e876ad 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"برای مشاهده ضربه بزنید"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"خطا در ذخیره‌سازی ضبط صفحه‌نمایش"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"خطا هنگام شروع ضبط صفحه‌نمایش"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ضبط‌کننده مشکل"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"درحال پردازش کردن ضبط مشکل"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"اعلان جاری مربوط به جلسه جمع‌آوری مشکل"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"هنگام ذخیره کردن ضبط مشکل، خطایی پیش آمد"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"هنگام شروع ضبط مشکل، خطایی پیش آمد"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"درحال مشاهده در حالت تمام‌صفحه"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"برای خروج، از بالای صفحه تند به‌پایین بکشید."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"متوجه‌ام"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"برگشت"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"صفحهٔ اصلی"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ذخیره‌شده"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"قطع اتصال"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کردن"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره به‌طور خودکار روشن شود"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ویژگی‌هایی مثل «هم‌رسانی سریع» و «پیدا کردن دستگاهم» از بلوتوث استفاده می‌کنند"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوتوث فردا صبح روشن خواهد شد"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"اشتراک صدا"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"درحال اشتراک‌گذاری صدا"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"هم‌رسانی صدا"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"درحال هم‌رسانی صدا"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"کدام بخش تجربه استفاده از دستگاه تحت‌تأثیر قرار گرفت؟"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"انتخاب نوع مشکل"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ضبط صفحه‌نمایش"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"عملکرد"</item>
-    <item msgid="1627504621139124393">"واسط کاربر"</item>
-    <item msgid="8309220355268900335">"باتری"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"عملکرد"</string>
+    <string name="user_interface" msgid="3712869377953950887">"میانای کاربر"</string>
+    <string name="thermal" msgid="6758074791325414831">"حرارتی"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"حالت یک‌دستی"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سمعک"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"اتصال قطع شد"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سمعک"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"جفت کردن دستگاه جدید"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string>
@@ -634,7 +662,7 @@
     <string name="volume_panel_hint_muted" msgid="1124844870181285320">"بی‌صدا شد"</string>
     <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"لرزش"</string>
     <string name="media_output_label_title" msgid="872824698593182505">"درحال پخش <xliff:g id="LABEL">%s</xliff:g> در"</string>
-    <string name="media_output_title_without_playing" msgid="3825663683169305013">"صدا در این دستگاه پخش می‌شود:"</string>
+    <string name="media_output_title_without_playing" msgid="3825663683169305013">"صدا کجا پخش خواهد شد:"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"تماس برقرار است"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"تنظیم‌کننده واسط کاربری سیستم"</string>
     <string name="status_bar" msgid="4357390266055077437">"نوار وضعیت"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"دکمه <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"ابتدا"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"برگشت"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"پیکان رو به‌بالا"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"پیکان روبه‌پایین"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"پیکان چپ"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"پیکان راست"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"مرکز"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"برای اطلاعات بیشتر ضربه بزنید"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"هشداری تنظیم نشده"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"وارد کردن قفل صفحه"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"حسگر اثرانگشت"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"اصالت‌سنجی کردن"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"وارد شدن به دستگاه"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"تاشده"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"تانشده"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> باتری باقی مانده است"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"شارژ باتری قلم <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"قلم را به شارژر وصل کنید"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"باتری قلم ضعیف است"</string>
     <string name="video_camera" msgid="7654002575156149298">"دوربین ویدیویی"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده می‌کند (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"سیستم"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"چندوظیفگی"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ورودی"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"میان‌برهای برنامه"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"دسترس‌پذیری"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"میان‌برهای صفحه‌کلید"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میان‌برها"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پس‌زمینه صفحه‌کلید"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏سطح %1$d از %2$d"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 459bec9..2291d19 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Katso napauttamalla"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Virhe näyttötallenteen tallentamisessa"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Virhe näytön tallennuksen aloituksessa"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Ongelman tallentaja"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Käsittely: Ongelman tallennus"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongelmankeräykseen liittyvä ilmoitus taustalla jatkuvasta toiminnasta"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Virhe ongelman tallenteen tallentamisessa"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Virhe ongelman tallentamisen aloituksessa"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Koko näytön tilassa"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Sulje palkki pyyhkäisemällä alas ruudun ylälaidasta."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Selvä"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Takaisin"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Aloitus"</string>
@@ -201,7 +228,7 @@
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Sormenjälkiavauksen määritys epäonnistui. Siirry asetuksiin ja yritä uudelleen."</string>
     <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Ota kasvojentunnistusavaus uudelleen käyttöön"</string>
     <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Kasvojentunnistusavaus"</string>
-    <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Kasvojentunnistusavauksen käyttöönotto"</string>
+    <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Kasvojen&amp;shy;tunnistus&amp;shy;avauksen käyttöönotto"</string>
     <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Jos haluat ottaa kasvojentunnistusavauksen uudelleen käyttöön, nykyinen kasvomalli poistetaan.\n\nJos haluat avata puhelimen lukituksen kasvoilla, sinun on otettava ominaisuus uudelleen käyttöön."</string>
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Kasvojentunnistusavauksen käyttöönotto epäonnistui. Siirry asetuksiin ja yritä uudelleen."</string>
     <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Kosketa sormenjälkitunnistinta"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkaise yhteys"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivoi"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Paikanna laite ja tietyt muut ominaisuudet käyttävät Bluetoothia"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth menee päälle huomisaamuna"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audionjako"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audiota jaetaan"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Suorituskyky"</item>
-    <item msgid="1627504621139124393">"Käyttöliittymä"</item>
-    <item msgid="8309220355268900335">"Akku"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Suorituskyky"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Käyttöliittymä"</string>
+    <string name="thermal" msgid="6758074791325414831">"Lämpökamera"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuulolaitteet"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiivinen"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Yhteys katkaistu"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuulolaitteet"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Muodosta uusi laitepari"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Esiasetusta ei voitu muuttaa"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Esiasetus"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Livetekstitys"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kumotaanko laitteen mikrofonin esto?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kumotaanko laitteen kameran esto?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kumotaanko laitteen kameran ja mikrofonin esto?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Painike <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Takaisin"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Ylänuoli"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Alanuoli"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vasen nuoli"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Oikea nuoli"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Keskelle"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Välilyönti"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Saat lisätietoja napauttamalla"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ei herätyksiä"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"käytä näytön lukitustapaa"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sormenjälkitunnistin"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"todentaaksesi"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"avataksesi laitteen"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"taitettu"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"taittamaton"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akkua jäljellä <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Näyttökynän akku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Yhdistä näyttökynä laturiin"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Näyttökynän akku vähissä"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> käytti tätä äskettäin (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Tämän käytössä: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> käytti tätä äskettäin (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Järjestelmä"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitaskaus"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Syöte"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Sovellusten pikakuvakkeet"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Saavutettavuus"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 0eab07e..fbd16d4 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Touchez pour afficher"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Erreur d\'enregistrement de l\'écran"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Une erreur s\'est produite lors du démarrage de l\'enregistrement d\'écran"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Enregistreur de problèmes"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Trait. de l\'enreg. du problème"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notification continue pour une session de collecte d\'un problème"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Erreur lors de l\'enregistrement du problème"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Erreur lors du démarrage de l\'enregistrement du problème"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Affichage plein écran"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Pour quitter, balayez du haut vers le bas."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Précédent"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Domicile"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Déconnecter"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"Activer"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les fonctionnalités comme Partage rapide et Localiser mon appareil utilisent le Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth s\'activera demain matin"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Partage de l\'audio en cours…"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performance"</item>
-    <item msgid="1627504621139124393">"Interface utilisateur"</item>
-    <item msgid="8309220355268900335">"Pile"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performance"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string>
+    <string name="thermal" msgid="6758074791325414831">"Thermique"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actives"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectées"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string>
@@ -618,7 +648,7 @@
     <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Contrôle du bruit"</string>
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Son spatial"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Désactivé"</string>
-    <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixé"</string>
+    <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Activé"</string>
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Touchez pour modifier le mode de sonnerie"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"désactiver le son"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Accueil"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Précédent"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flèche vers le haut"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flèche vers le bas"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flèche vers la gauche"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flèche vers la droite"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrer"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Espace"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Entrée"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Touchez pour en savoir plus"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Aucune alarme définie"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"entrer verrouillage de l\'écran"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Capteur d\'empreintes digitales"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"authentifier"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
@@ -1209,8 +1236,8 @@
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur"</string>
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# application est active}one{# application est active}many{# d\'applications sont actives}other{# applications sont actives}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelle information"</string>
-    <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applications actives"</string>
-    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applications sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string>
+    <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string>
+    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Charge restante de la pile : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pile du stylet <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Pile du stylet faible"</string>
     <string name="video_camera" msgid="7654002575156149298">"Caméra"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Système"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis des applis"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index d3983b9..55c39dc 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Appuyez pour afficher"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Erreur lors de l\'enregistrement de l\'écran"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Erreur lors du démarrage de l\'enregistrement de l\'écran"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Enregistreur de problèmes"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Enregistrement du problème"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notification d\'activité en cours concernant la session d\'enregistrement d\'un problème"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Erreur lors de l\'enregistrement du problème"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Erreur lors du démarrage de l\'enregistrement du problème"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Affichage en plein écran"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Pour quitter, balayez l\'écran du haut vers le bas."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Retour"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Accueil"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"dissocier"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activer"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Certaines fonctionnalités, telles que Quick Share et Localiser mon appareil, utilisent le Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth sera activé demain matin"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio partagé"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performances"</item>
-    <item msgid="1627504621139124393">"Interface utilisateur"</item>
-    <item msgid="8309220355268900335">"Batterie"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performances"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string>
+    <string name="thermal" msgid="6758074791325414831">"Thermique"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actifs"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectés"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Bouton <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Accueil"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Précédent"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Flèche vers le haut"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Flèche vers le bas"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Flèche vers la gauche"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Flèche vers la droite"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centre"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Espace"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Entrée"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Appuyer pour en savoir plus"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Pas d\'alarme définie"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"accéder au verrouillage de l\'écran"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Lecteur d\'empreinte digitale"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"s\'authentifier"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
@@ -1212,7 +1239,7 @@
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string>
     <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnement, mais peut également affecter l\'autonomie de la batterie."</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string>
-    <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string>
+    <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêté"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string>
     <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copié"</string>
     <string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de batterie restante"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batterie du stylet à <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"La batterie du stylet est faible"</string>
     <string name="video_camera" msgid="7654002575156149298">"Caméra"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En cours d\'utilisation par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Système"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitâche"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrée"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Raccourcis d\'application"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 23c124c..fcdd9f0 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -58,8 +58,8 @@
   </string-array>
   <string-array name="tile_states_flashlight">
     <item msgid="3465257127433353857">"Indisponible"</item>
-    <item msgid="5044688398303285224">"Désactivée"</item>
-    <item msgid="8527389108867454098">"Activée"</item>
+    <item msgid="5044688398303285224">"Désactivé"</item>
+    <item msgid="8527389108867454098">"Activé"</item>
   </string-array>
   <string-array name="tile_states_rotation">
     <item msgid="4578491772376121579">"Indisponible"</item>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 6c494c1..a85b9ec 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Toca para ver o contido"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Produciuse un erro ao gardar a gravación da pantalla"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Produciuse un erro ao iniciar a gravación da pantalla"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Gravadora de problemas"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Procesando gravación problema"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificación de actividade en curso para unha sesión de rexistro dun problema"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Produciuse un erro ao gardar a gravación do problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Produciuse un erro ao iniciar a gravación do problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Vendo pantalla completa"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Para saír, pasa o dedo cara abaixo desde a parte superior."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Entendido"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Volver"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Inicio"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"As funcións como Quick Share e Localizar o meu dispositivo utilizan o Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth activarase mañá á mañá"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio compartido"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartindo audio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Rendemento"</item>
-    <item msgid="1627504621139124393">"Interface de usuario"</item>
-    <item msgid="8309220355268900335">"Batería"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Rendemento"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interface de usuario"</string>
+    <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo novo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Non se puido actualizar a configuración predeterminada"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Configuración predeterminada"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Subtítulos instantáneos"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Queres desbloquear o micrófono do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Queres desbloquear a cámara do dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Queres desbloquear a cámara e o micrófono do dispositivo?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Inicio"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Volver"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Frecha arriba"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Frecha abaixo"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Frecha esquerda"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Frecha dereita"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centro"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Espazo"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Intro"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca para obter máis información"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Sen alarmas postas"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"introducir o bloqueo de pantalla"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impresión dixital"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"poñer o dispositivo"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dispositivo pregado"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dispositivo despregado"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batería restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería do lapis óptico: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta o lapis óptico a un cargador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"O lapis óptico ten pouca batería"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"En uso recentemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"En uso recentemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefa"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atallos de aplicacións"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidade"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index f2516c9..11846b3 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"જોવા માટે ટૅપ કરો"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"સ્ક્રીન રેકોર્ડિંગ સાચવવામાં ભૂલ આવી"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"સ્ક્રીનને રેકૉર્ડ કરવાનું શરૂ કરવામાં ભૂલ"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"સમસ્યા રેકોર્ડર"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"સમસ્યા રેકોર્ડિંગ ચાલુ છે"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"કોઈ સમસ્યા સંગ્રહના સત્ર માટે ચાલુ નોટિફિકેશન"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"સમસ્યા રેકોર્ડિંગને સાચવવામાં ભૂલ આવી"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"સમસ્યા રેકોર્ડિંગને શરૂ કરવામાં ભૂલ આવી"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"પૂર્ણ સ્ક્રીન જોઈ રહ્યાં છે"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"બહાર નીકળવા માટે, સૌથી ઉપરથી નીચેની તરફ સ્વાઇપ કરો."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"સમજાઈ ગયું"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"પાછળ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"હોમ"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"સાચવેલું"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ડિસ્કનેક્ટ કરો"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"સક્રિય કરો"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ક્વિક શેર અને Find My Device જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"બ્લૂટૂથ આવતીકાલે સવારે ચાલુ થશે"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ઑડિયો શેરિંગ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ઑડિયો શેર કરી રહ્યાં છીએ"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ઑડિયો શેર કરો"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ઑડિયો શેર કરી રહ્યાં છીએ"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ડિવાઇસ સંબંધી તમારા અનુભવના કયા ભાગને અસર થઈ હતી?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"સમસ્યાનો પ્રકાર પસંદ કરો"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"સ્ક્રીન રેકોર્ડ કરો"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"પર્ફોર્મન્સ"</item>
-    <item msgid="1627504621139124393">"યૂઝર ઇન્ટરફેસ"</item>
-    <item msgid="8309220355268900335">"બૅટરી"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"પર્ફોર્મન્સ"</string>
+    <string name="user_interface" msgid="3712869377953950887">"યૂઝર ઇન્ટરફેસ"</string>
+    <string name="thermal" msgid="6758074791325414831">"થર્મલ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"એક-હાથે વાપરો મોડ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"સક્રિય"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ડિસ્કનેક્ટેડ છે"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"નવા ડિવાઇસ સાથે જોડાણ કરવા માટે ક્લિક કરો"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"બટન <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ઉપરની ઍરો કી"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"નીચેની ઍરો કી"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ડાબી ઍરો કી"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"જમણી ઍરો કી"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"વધુ માહિતી માટે ટૅપ કરો"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"કોઈ અલાર્મ સેટ નથી"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"સ્ક્રીન લૉક દાખલ કરો"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ફિંગરપ્રિન્ટ સેન્સર"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ખાતરી કરો"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ડિવાઇસ અનલૉક કરો"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ફોલ્ડ કરેલું"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"અનફોલ્ડ કરેલું"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> બૅટરી બાકી છે"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"સ્ટાઇલસની બૅટરી <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"તમારા સ્ટાઇલસને ચાર્જર સાથે કનેક્ટ કરો"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"સ્ટાઇલસની બૅટરીમાં ચાર્જ ઓછો છે"</string>
     <string name="video_camera" msgid="7654002575156149298">"વીડિયો કૅમેરા"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) દ્વારા તાજેતરમાં ઉપયોગ કરવામાં આવ્યો"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) દ્વારા ઉપયોગ ચાલુ છે"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) દ્વારા તાજેતરમાં ઉપયોગ કરવામાં આવ્યો"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"સિસ્ટમ"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"એકથી વધુ કાર્યો કરવા"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ઇનપુટ"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ઍપ શૉર્ટકટ"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ઍક્સેસિબિલિટી"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"કીબોર્ડ શૉર્ટકટ"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 0e6e77d..dcd3b6c 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"देखने के लिए टैप करें"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"स्क्रीन रिकॉर्डिंग सेव करते समय गड़बड़ी हुई"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन को रिकॉर्ड करने में गड़बड़ी आ रही है"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"समस्या का डेटा सेव करने वाला टूल"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"समस्या का डेटा प्रोसेस हो रहा"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"समस्या का डेटा इकट्ठा करने के लिए बैकग्राउंड में जारी गतिविधि की सूचना"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"समस्या का डेटा सेव करते समय गड़बड़ी हुई"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"समस्या का डेटा सेव करने की प्रोसेस शुरू करते समय गड़बड़ी हुई"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"फ़ुल स्क्रीन मोड पर देखा जा रहा है"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"बाहर निकलने के लिए, सबसे ऊपर से नीचे की ओर स्वाइप करें."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"ठीक है"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"वापस जाएं"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"होम"</string>
@@ -178,7 +205,7 @@
     <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"गलत पैटर्न"</string>
     <string name="biometric_dialog_wrong_password" msgid="69477929306843790">"गलत पासवर्ड"</string>
     <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"आपकी कोशिशें बहुत बार गलत हुई हैं.\nआप <xliff:g id="NUMBER">%d</xliff:g> सेकंड में फिर से कोशिश कर सकते हैं."</string>
-    <string name="work_challenge_emergency_button_text" msgid="8946588434515599288">"आपातकालीन कॉल"</string>
+    <string name="work_challenge_emergency_button_text" msgid="8946588434515599288">"आपातकालीन कॉल करें"</string>
     <string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"फिर से कोशिश करें. आप <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> में से <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> बार कोशिश कर चुके हैं."</string>
     <string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"आपका डेटा मिटा दिया जाएगा"</string>
     <string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"अगर आप फिर से गलत पैटर्न डालते हैं, तो इस डिवाइस का डेटा मिटा दिया जाएगा."</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव किया गया"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिसकनेक्ट करें"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"चालू करें"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाए"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेयर और Find My Device जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ कल सुबह चालू होगा"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडियो शेयर करने की सुविधा"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडियो शेयर किया जा रहा है"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -295,7 +325,7 @@
     <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रीन सेवर"</string>
     <string name="quick_settings_camera_label" msgid="5612076679385269339">"कैमरे का ऐक्सेस"</string>
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक्रोफ़ोन का ऐक्सेस"</string>
-    <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध"</string>
+    <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध है"</string>
     <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"ब्लॉक किया गया है"</string>
     <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मीडिया डिवाइस"</string>
     <string name="quick_settings_user_title" msgid="8673045967216204537">"उपयोगकर्ता"</string>
@@ -358,24 +388,24 @@
     <string name="qs_record_issue_start" msgid="2979831312582567056">"शुरू करें"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"रोकें"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"गड़बड़ी की रिपोर्ट"</string>
-    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"आपके डिवाइस की कौनसी सुविधा पर असर पड़ा था?"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"आपके डिवाइस की किस सुविधा में समस्या आ रही थी?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्या का टाइप चुनें"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रिकॉर्डर"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"परफ़ॉर्मेंस"</item>
-    <item msgid="1627504621139124393">"यूज़र इंटरफ़ेस"</item>
-    <item msgid="8309220355268900335">"बैटरी"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"परफ़ॉर्मेंस"</string>
+    <string name="user_interface" msgid="3712869377953950887">"यूज़र इंटरफ़ेस"</string>
+    <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ऐक्टिव"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिसकनेक्ट हो गया"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"कान की मशीनें"</string>
-    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नया डिवाइस जोड़ें"</string>
+    <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"कान की नई मशीन जोड़ें"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट नहीं किया जा सका"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"लाइव कैप्शन की सुविधा"</string>
-    <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आपको डिवाइस का माइक्रोफ़ोन अनब्लॉक करना है?"</string>
-    <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आपको डिवाइस का कैमरा अनब्लॉक करना है?"</string>
+    <string name="live_caption_title" msgid="8916875614623730005">"लाइव कैप्शन"</string>
+    <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आपको माइक्रोफ़ोन का ऐक्सेस अनब्लॉक करना है?"</string>
+    <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आपको कैमरे का ऐक्सेस अनब्लॉक करना है?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"क्या आप डिवाइस का कैमरा और माइक्रोफ़ोन अनब्लॉक करना चाहते हैं?"</string>
     <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"ऐसा करने से, सभी ऐप्लिकेशन और सेवाओं के लिए माइक्रोफ़ोन का ऐक्सेस अनब्लॉक हो जाएगा और वे इसका इस्तेमाल कर पाएंगी."</string>
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"ऐसा करने से, सभी ऐप्लिकेशन और सेवाओं के लिए कैमरे का ऐक्सेस अनब्लॉक हो जाएगा और वे इसका इस्तेमाल कर पाएंगी."</string>
@@ -594,7 +624,7 @@
     <string name="screen_pinning_negative" msgid="6882816864569211666">"नहीं, रहने दें"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"ऐप्लिकेशन पिन किया गया"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"ऐप्लिकेशन अनपिन किया गया"</string>
-    <string name="stream_voice_call" msgid="7468348170702375660">"कॉल करें"</string>
+    <string name="stream_voice_call" msgid="7468348170702375660">"कॉल"</string>
     <string name="stream_system" msgid="7663148785370565134">"सिस्‍टम"</string>
     <string name="stream_ring" msgid="7550670036738697526">"रिंग"</string>
     <string name="stream_music" msgid="2188224742361847580">"मीडिया"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"बटन <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप ऐरो"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन ऐरो"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ़्ट ऐरो"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट ऐरो"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"मध्य तीर"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -997,7 +1022,7 @@
     <string name="accessibility_magnification_right_handle" msgid="9055988237319397605">"दायां हैंडल"</string>
     <string name="accessibility_magnification_bottom_handle" msgid="6531646968813821258">"नीचे का हैंडल"</string>
     <string name="accessibility_magnification_settings_panel_description" msgid="8174187340747846953">"ज़ूम करने की सुविधा वाली सेटिंग"</string>
-    <string name="accessibility_magnifier_size" msgid="3038755600030422334">"ज़ूम करने की सुविधा का साइज़"</string>
+    <string name="accessibility_magnifier_size" msgid="3038755600030422334">"ज़ूम का साइज़"</string>
     <string name="accessibility_magnification_zoom" msgid="4222088982642063979">"ज़ूम करें"</string>
     <string name="accessibility_magnification_medium" msgid="6994632616884562625">"मध्यम"</string>
     <string name="accessibility_magnification_small" msgid="8144502090651099970">"छोटा"</string>
@@ -1112,7 +1137,7 @@
     <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> डिवाइस चुने गए"</string>
     <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(डिसकनेक्ट हो गया)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"स्विच नहीं किया जा सकता. फिर से कोशिश करने के लिए टैप करें."</string>
-    <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"किसी डिवाइस को कनेक्ट करें"</string>
+    <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"कोई डिवाइस कनेक्ट करें"</string>
     <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string>
     <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string>
     <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करना बंद करें"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ज़्यादा जानकारी के लिए टैप करें"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"कोई अलार्म सेट नहीं है"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"स्क्रीन लॉक डालें"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फ़िंगरप्रिंट सेंसर"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"पुष्टि करें"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिवाइस की होम स्क्रीन पर जाएं"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"डिवाइस फ़ोल्ड किया गया"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"डिवाइस अनफ़ोल्ड किया गया"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> बैटरी बची है"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलस की बैटरी <xliff:g id="PERCENTAGE">%s</xliff:g> है"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"अपने स्टाइलस को चार्ज करें"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलस की बैटरी कम है"</string>
     <string name="video_camera" msgid="7654002575156149298">"वीडियो कैमरा"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"हाल ही में, <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ने इस्तेमाल किया"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) पर इस्तेमाल किया जा रहा है"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"हाल ही में, <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ने इस्तेमाल किया"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"सिस्टम"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"मल्टीटास्किंग (एक साथ कई काम करना)"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ऐप शॉर्टकट"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"सुलभता"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्च शॉर्टकट"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 2e6ae04..1eff7d4 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Dodirnite za prikaz"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Pogreška prilikom spremanja snimke zaslona"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Pogreška prilikom pokretanja snimanja zaslona"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Snimač poteškoća"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Obrada snimke poteškoće"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Obavijest o aktivnosti u pozadini za sesiju prikupljanja poteškoća"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Pogreška pri spremanju snimke poteškoće"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Pogreška pri pokretanju snimke poteškoće"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Gledanje preko cijelog zaslona"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Za izlaz prijeđite prstom od vrha prema dolje."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Shvaćam"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Natrag"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Početna"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekini vezu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviraj"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovno sutra"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Značajke kao što su brzo dijeljenje i Pronađi moj uređaj koriste Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zajedničko slušanje"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zajedničko slušanje"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Dijeli zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zajedničko slušanje"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doživljaja na uređaju to utjecalo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Izvedba"</item>
-    <item msgid="1627504621139124393">"Korisničko sučelje"</item>
-    <item msgid="8309220355268900335">"Baterija"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Izvedba"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termalno"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušna pomagala"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nije povezano"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušna pomagala"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unaprijed definirana postavka"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Automatski titlovi"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite li deblokirati mikrofon uređaja?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite li deblokirati kameru uređaja?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite li deblokirati kameru i mikrofon uređaja?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tipka <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Početak"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Natrag"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strelica prema gore"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strelica prema dolje"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strelica lijevo"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strelica desno"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Sredina"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Razmaknica"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Unos"</string>
@@ -933,7 +955,7 @@
     <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Način Ne uznemiravaj uključilo je automatsko pravilo ili aplikacija."</string>
     <string name="running_foreground_services_title" msgid="5137313173431186685">"Izvođenje aplikacija u pozadini"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"Dodirnite da biste vidjeli pojedinosti o potrošnji baterije i podatkovnom prometu"</string>
-    <string name="mobile_data_disable_title" msgid="5366476131671617790">"Isključiti mobilne podatke?"</string>
+    <string name="mobile_data_disable_title" msgid="5366476131671617790">"Želite li isključiti mobilne podatke?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup mobilnim podacima ili internetu putem operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem Wi-Fija."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš mobilni operater"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Vratiti se na mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>?"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nema nijednog alarma"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"unesite zaključavanje zaslona"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor otiska prsta"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentificirali"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"pristupili uređaju"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostalo je <xliff:g id="PERCENTAGE">%s</xliff:g> baterije"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija olovke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Slaba baterija pisaljke"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Koristi: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sustav"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Obavljanje više zadataka"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Unos"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečaci aplikacija"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index d5649b7..c0905b5 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Koppintson a megtekintéshez"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Hiba történt a képernyőrögzítés mentése során"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Hiba a képernyőrögzítés indításakor"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Problémafelvevő"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Problémafelvétel feldolgozása…"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Folyamatban lévő értesítés egy problémagyűjtési munkamenethez"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Hiba történt a problémafelvétel mentésekor"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Hiba történt a problémafelvétel elindításakor"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Megtekintése teljes képernyőn"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Kilépéshez csúsztassa ujját fentről lefelé."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Értem"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Vissza"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Főoldal"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"leválasztás"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiválás"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Egyes funkciók (például a Quick Share és a Készülékkereső) Bluetootht használnak"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"A Bluetooth holnap reggel bekapcsol"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hang megosztása"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Hang megosztása…"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -292,7 +322,7 @@
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string>
-    <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyővédő"</string>
+    <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyőkímélő"</string>
     <string name="quick_settings_camera_label" msgid="5612076679385269339">"Hozzáférés a kamerához"</string>
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonelérés"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Rendelkezésre áll"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Képernyőrögzítés"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Teljesítmény"</item>
-    <item msgid="1627504621139124393">"Kezelőfelület"</item>
-    <item msgid="8309220355268900335">"Akkumulátor"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Teljesítmény"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Kezelőfelület"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termikus"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hallókészülékek"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktív"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Leválasztva"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hallókészülékek"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Új eszköz párosítása"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string>
@@ -596,7 +626,7 @@
     <string name="screen_pinning_exit" msgid="4553787518387346893">"Alkalmazás kitűzése megszüntetve"</string>
     <string name="stream_voice_call" msgid="7468348170702375660">"Telefonhívás"</string>
     <string name="stream_system" msgid="7663148785370565134">"Rendszer"</string>
-    <string name="stream_ring" msgid="7550670036738697526">"Csörgetés"</string>
+    <string name="stream_ring" msgid="7550670036738697526">"Csörgés"</string>
     <string name="stream_music" msgid="2188224742361847580">"Média"</string>
     <string name="stream_alarm" msgid="16058075093011694">"Ébresztő"</string>
     <string name="stream_notification" msgid="7930294049046243939">"Értesítés"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> gomb"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Kezdőképernyő"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Vissza"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Felfelé nyíl"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Lefelé nyíl"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Balra nyíl"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Jobbra nyíl"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Középre"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Szóköz"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Koppintással további információkat érhet el."</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nincs ébresztés"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"képernyőzár megadása"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Ujjlenyomat-érzékelő"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"a hitelesítéshez"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"eszköz megadásához"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"összehajtva"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kihajtva"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Akkumulátor töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Érintőceruza töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tegye töltőre az érintőceruzát"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Az érintőceruza töltöttsége alacsony"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Legutóbb használta: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Használatban a következő által: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Legutóbb használta: <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Rendszer"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Bevitel"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Alkalmazás-parancsikonok"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
+    <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>
+    <!-- no translation found for touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 3860921..41593d8 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Հպեք՝ դիտելու համար"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Չհաջողվեց պահել էկրանի տեսագրությունը"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Չհաջողվեց սկսել տեսագրումը"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Խնդիրների տեսագրիչ"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Մշակում ենք տեսագրությունը"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Խնդրի տեսագրման մասին ընթացիկ ծանուցում"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Չհաջողվեց պահել տեսագրությունը"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Չհաջողվեց սկսել տեսագրումը"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Լիաէկրան դիտակերպ"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Դուրս գալու համար վերևից մատը սահեցրեք դեպի ներքև։"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Եղավ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Հետ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Տուն"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Պահված է"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"անջատել"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ակտիվացնել"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth-ն օգտագործում են, օրինակ, Quick Share և «Գտնել իմ սարքը» գործառույթները"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Աուդիոյի փոխանցում"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Աուդիոն փոխանցվում է"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Սարքի ո՞ր մասի հետ է կապված խնդիրը։"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ընտրեք խնդրի տեսակը"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Էկրանի տեսագրում"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Աշխատանք"</item>
-    <item msgid="1627504621139124393">"Միջերես"</item>
-    <item msgid="8309220355268900335">"Մարտկոց"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Արդյունավետություն"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Օգտատիրական ինտերֆեյս"</string>
+    <string name="thermal" msgid="6758074791325414831">"Ջերմատեսիլ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Մեկ ձեռքի ռեժիմ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Լսողական սարքեր"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ակտիվ է"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Անջատված է"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Լսողական սարքեր"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Նոր սարքի զուգակցում"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Սեղմեք՝ նոր սարք զուգակցելու համար"</string>
@@ -594,9 +624,9 @@
     <string name="screen_pinning_negative" msgid="6882816864569211666">"Ոչ"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"Հավելվածն ամրացվեց"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"Հավելվածն ապամրացվեց"</string>
-    <string name="stream_voice_call" msgid="7468348170702375660">"Զանգ"</string>
+    <string name="stream_voice_call" msgid="7468348170702375660">"Զանգելը"</string>
     <string name="stream_system" msgid="7663148785370565134">"Համակարգ"</string>
-    <string name="stream_ring" msgid="7550670036738697526">"Զանգ"</string>
+    <string name="stream_ring" msgid="7550670036738697526">"Զանգ ստանալը"</string>
     <string name="stream_music" msgid="2188224742361847580">"Մեդիա"</string>
     <string name="stream_alarm" msgid="16058075093011694">"Զարթուցիչ"</string>
     <string name="stream_notification" msgid="7930294049046243939">"Ծանուցում"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> կոճակ"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Գլխավոր էջ"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Հետ"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Վերև սլաք"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Ներքև սլաք"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Ձախ սլաք"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Աջ սլաք"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Կենտրոն"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Բացատ"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Մուտք"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Հպեք՝ ավելին իմանալու համար"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Զարթուցիչ դրված չէ"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ապակողպել էկրանը"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Մատնահետքի սկաներ"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"նույնականացնել"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"նշել սարքը"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ծալված"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"բացված"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ստիլուսի մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ձեր ստիլուսը միացրեք լիցքավորիչի"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Ստիլուսի մարտկոցի լիցքի ցածր մակարդակ"</string>
     <string name="video_camera" msgid="7654002575156149298">"Տեսախցիկ"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Վերջերս օգտագործվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Օգտագործվում է <xliff:g id="APP_NAME">%1$s</xliff:g>ի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Վերջերս օգտագործվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Համակարգ"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Բազմախնդրություն"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ներածում"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Հավելվածի դյուրանցումներ"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Հատուկ գործառույթներ"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Ստեղնային դյուրանցումներ"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index b0ca07e..831adf9 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Ketuk untuk melihat"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Terjadi error saat menyimpan rekaman layar"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Terjadi error saat memulai perekaman layar"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Perekam Masalah"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Memproses rekaman masalah"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notifikasi berkelanjutan untuk sesi pengumpulan masalah"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Terjadi error saat menyimpan rekaman masalah"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Terjadi error saat memulai perekaman masalah"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Melihat layar penuh"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Untuk keluar, geser layar ke bawah dari atas."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Oke"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Kembali"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Utama"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan koneksi"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Fitur seperti Quick Share dan Temukan Perangkat Saya menggunakan Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dinyalakan besok pagi"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Berbagi Audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berbagi Audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Bagikan audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Berbagi audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performa"</item>
-    <item msgid="1627504621139124393">"Antarmuka Pengguna"</item>
-    <item msgid="8309220355268900335">"Baterai"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performa"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Antarmuka Pengguna"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Terputus"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Alat bantu dengar"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat memperbarui preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Teks Otomatis"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Berhenti memblokir mikrofon perangkat?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Berhenti memblokir kamera perangkat?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Berhenti memblokir kamera dan mikrofon perangkat?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tombol <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Panah atas"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Panah bawah"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Panah kiri"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Panah kanan"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ketuk untuk informasi selengkapnya"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Alarm tidak disetel"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"masukkan kunci layar"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor sidik jari"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentikasi"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"masukkan perangkat"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ditutup"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dibuka"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Baterai tersisa <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterai stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hubungkan stilus ke pengisi daya"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Baterai stilus lemah"</string>
     <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Baru saja digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Sedang digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Baru saja digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan aplikasi"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aksesibilitas"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 02bc343..a73bd7a 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Ýttu til að skoða"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Villa við að vista skjáupptöku"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Villa við að hefja upptöku skjás"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Upptökutæki fyrir vandamál"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Vinnur úr upptöku af vandamáli"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Áframhaldandi tilkynning fyrir lotu vandamálasafns"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Villa kom upp við að vista upptöku af vandamáli"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Villa kom upp við að hefja upptöku af vandamáli"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Notar allan skjáinn"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Strjúktu niður frá efsta hluta skjásins til að hætta."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Ég skil"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Til baka"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Heim"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"aftengja"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"virkja"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Eiginleikar eins og Flýtideiling og Finna tækið mitt nota Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Kveikt verður á Bluetooth í fyrramálið"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hljóði deilt"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deilir hljóði"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Deila hljóði"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deilir hljóði"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvað í tækjaupplifuninni varð fyrir áhrifum?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Veldu gerð vandamáls"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjáupptaka"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Afköst"</item>
-    <item msgid="1627504621139124393">"Notandaviðmót"</item>
-    <item msgid="8309220355268900335">"Rafhlaða"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Afköst"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Notandaviðmót"</string>
+    <string name="thermal" msgid="6758074791325414831">"Varmi"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Heyrnartæki"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Virk"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Aftengd"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Heyrnartæki"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Para nýtt tæki"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tókst ekki að uppfæra forstillingu"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forstilling"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Skjátextar í rauntíma"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Opna fyrir hljóðnema tækisins?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Opna fyrir myndavél tækisins?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Opna fyrir myndavél og hljóðnema tækisins?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Hnappur <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Til baka"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Ör upp"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Ör niður"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Ör til vinstri"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Ör til hægri"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Miðja"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Bilslá"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ýttu til að fá frekari upplýsingar"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Enginn vekjari"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"sláðu inn skjálás"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingrafaralesari"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"auðkenna"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"opna tæki"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"samanbrotið"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opið"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> hleðsla eftir á rafhlöðu"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pennarafhlaða <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tengdu pennann við hleðslutæki"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Rafhlaða pennans er að tæmast"</string>
     <string name="video_camera" msgid="7654002575156149298">"Kvikmyndatökuvél"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nýlega notað af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Í notkun í <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nýlega notað af <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Kerfi"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Fjölvinnsla"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inntak"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Flýtileiðir forrita"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aðgengi"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index eb90d58..a424f20 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tocca per visualizzare"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Errore durante il salvataggio della registrazione dello schermo"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Errore durante l\'avvio della registrazione dello schermo"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Registratore dei problemi"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Elaborazione registrazione…"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notifica continua per una sessione di raccolta di problemi"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Impossibile salvare la registrazione del problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Impossibile avviare la registrazione del problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visualizzazione a schermo intero"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Per uscire, scorri dall\'alto verso il basso."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Indietro"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnetti"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"attiva"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funzionalità come Quick Share e Trova il mio dispositivo usano il Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Il Bluetooth verrà attivato domani mattina"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Condivisione audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Condivisione audio in corso…"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Condividi audio"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Condivisione audio in corso…"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regis. dello schermo"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Prestazioni"</item>
-    <item msgid="1627504621139124393">"Interfaccia utente"</item>
-    <item msgid="8309220355268900335">"Batteria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Prestazioni"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interfaccia utente"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termico"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Apparecchi acustici"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Attivi"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnessi"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Protesi uditive"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Accoppia nuovo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string>
@@ -580,11 +608,11 @@
     <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Continua ad ascoltare"</string>
     <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Abbassa il volume"</string>
     <string name="screen_pinning_title" msgid="9058007390337841305">"L\'app è bloccata sullo schermo"</string>
-    <string name="screen_pinning_description" msgid="8699395373875667743">"La schermata rimane visibile finché non viene sganciata. Per sganciarla, tieni premuto Indietro e Panoramica."</string>
-    <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"La schermata rimane visibile finché non viene disattivato il blocco su schermo. Per disattivarlo, tocca e tieni premuto Indietro e Home."</string>
+    <string name="screen_pinning_description" msgid="8699395373875667743">"La schermata rimane visibile finché non viene sbloccata. Per sbloccarla, tieni premuto Indietro e Panoramica."</string>
+    <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"La schermata rimane visibile finché non viene sbloccata. Per sbloccarla, tocca e tieni premuto Indietro e Home."</string>
     <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Rimarrà visibile finché non viene sbloccata. Scorri verso l\'alto e tieni premuto per sbloccarla."</string>
-    <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"La schermata rimane visibile finché non viene sganciata. Per sganciarla, tieni premuto Panoramica."</string>
-    <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"La schermata rimane visibile finché non viene disattivato il blocco su schermo. Per disattivarlo, tocca e tieni premuto Home."</string>
+    <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"La schermata rimane visibile finché non viene sbloccata. Per sbloccarla, tieni premuto Panoramica."</string>
+    <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"La schermata rimane visibile finché non viene sbloccata. Per sbloccarla, tocca e tieni premuto Home."</string>
     <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"I dati personali potrebbero essere accessibili (ad esempio i contatti e i contenuti delle email)."</string>
     <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"L\'app bloccata sullo schermo potrebbe aprire altre app."</string>
     <string name="screen_pinning_toast" msgid="8177286912533744328">"Per sbloccare questa app, tocca e tieni premuti i pulsanti Indietro e Panoramica"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Pulsante <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home page"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Indietro"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Freccia su"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Freccia giù"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Freccia sinistra"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Freccia destra"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Al centro"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Spazio"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Invio"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tocca per ulteriori informazioni"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nessuna"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"inserisci blocco schermo"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensore di impronte digitali"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"effettuare l\'autenticazione"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"accedere al dispositivo"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"Piegato"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"Non piegato"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteria rimanente"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteria dello stilo: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connetti lo stilo a un caricabatterie"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Batteria stilo in esaurimento"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videocamera"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recentemente in uso da <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"In uso da <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recentemente in uso da <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Scorciatoie app"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilità"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 5fc6cde..4e0977e 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"יש להקיש כדי להציג"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"שגיאה בשמירה של הקלטת המסך"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"שגיאה בהפעלה של הקלטת המסך"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"בעיה במכשיר ההקלטה"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"מתבצע עיבוד של בעיית ההקלטה"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"התראה מתמשכת לסשן איסוף הבעיה"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"שגיאה בשמירה של בעיית ההקלטה"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"שגיאה בהפעלה של בעיית ההקלטה"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"צפייה במסך מלא"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"כדי לצאת, מחליקים למטה מהחלק העליון של המסך."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"הבנתי"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"חזרה"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"בית"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‏תכונות כמו \'שיתוף מהיר\' ו\'איפה המכשיר שלי\' משתמשות ב-Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"‏חיבור ה-Bluetooth יופעל מחר בבוקר"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"שיתוף אודיו"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"מתבצע שיתוף של האודיו"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"איזה חלק בחוויית השימוש שלך במכשיר הושפע?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"בחירה בסוג הבעיה"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"הקלטת המסך"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"ביצועים"</item>
-    <item msgid="1627504621139124393">"ממשק משתמש"</item>
-    <item msgid="8309220355268900335">"סוללה"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"ביצועים"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ממשק משתמש"</string>
+    <string name="thermal" msgid="6758074791325414831">"תרמי"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"מצב שימוש ביד אחת"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"מכשירי שמיעה"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"פעיל"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"מנותק"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"מכשירי שמיעה"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"התאמה של מכשיר חדש"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"צריך ללחוץ כדי להתאים מכשיר חדש"</string>
@@ -618,7 +648,7 @@
     <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"בקרת רעש"</string>
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"אודיו מרחבי"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"השבתה"</string>
-    <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב קבוע"</string>
+    <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב סטטי"</string>
     <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב אחר תנועות הראש"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string>
@@ -634,7 +664,7 @@
     <string name="volume_panel_hint_muted" msgid="1124844870181285320">"השתקה"</string>
     <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"רטט"</string>
     <string name="media_output_label_title" msgid="872824698593182505">"הפעלה של <xliff:g id="LABEL">%s</xliff:g> במכשיר"</string>
-    <string name="media_output_title_without_playing" msgid="3825663683169305013">"האודיו יופעל במכשיר"</string>
+    <string name="media_output_title_without_playing" msgid="3825663683169305013">"איפה האודיו יופעל"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"מתבצעת שיחה במכשיר"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
     <string name="status_bar" msgid="4357390266055077437">"שורת סטטוס"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"לחצן <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"דף הבית"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"הקודם"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"חץ למעלה"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"חץ למטה"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"חץ שמאלה"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"חץ ימינה"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"מרכז"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"רווח"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"יש להקיש כדי להציג מידע נוסף"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"לא הוגדרה"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"הזנת קוד נעילת המסך"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"חיישן טביעות אצבע"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"אימות"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"הזנת מכשיר"</string>
@@ -1201,7 +1228,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"‏ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string>
-    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"‏כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות זאת בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"‏כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות את זה בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string>
     <string name="turn_off_airplane_mode" msgid="8425587763226548579">"השבתה של מצב טיסה"</string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"אפליקציית <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את הלחצן הבא"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"להוסיף לחצן"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"מצב מקופל"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"מצב לא מקופל"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"‏%1$s‏ / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"רמת הטעינה שנותרה בסוללה: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"רמת הטעינה בסוללה של הסטיילוס: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"כדאי לחבר את הסטיילוס למטען"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"הסוללה של הסטיילוס חלשה"</string>
     <string name="video_camera" msgid="7654002575156149298">"מצלמת וידאו"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"נעשה שימוש לאחרונה על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"בשימוש על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"נעשה שימוש לאחרונה על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"מערכת"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ריבוי משימות"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"קלט"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"קיצורי דרך של אפליקציות"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"נגישות"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"מקשי קיצור"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏רמה %1$d מתוך %2$d"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 81fa094..6c8504bb 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"タップすると表示されます"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"画面の録画の保存中にエラーが発生しました"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"画面の録画中にエラーが発生しました"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"問題記録ツール"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"問題の記録を処理しています"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"問題の収集セッションに関する進行中の通知"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"問題の記録の保存中にエラーが発生しました"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"問題の記録の開始中にエラーが発生しました"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"全画面表示"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"終了するには、上から下にスワイプします。"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"戻る"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"ホーム"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存済み"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"接続を解除"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"有効化"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share や「デバイスを探す」などの機能は Bluetooth を使用します"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音声の共有"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"音声を共有中"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"デバイスのどの部分が影響を受けましたか?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"問題の種類を選択する"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"スクリーン レコード"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"パフォーマンス"</item>
-    <item msgid="1627504621139124393">"ユーザー インターフェース"</item>
-    <item msgid="8309220355268900335">"バッテリー"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"パフォーマンス"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ユーザー インターフェース"</string>
+    <string name="thermal" msgid="6758074791325414831">"温度"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"片手モード"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"補聴器"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"アクティブ"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"未接続"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"補聴器"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"新しいデバイスとペア設定"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"クリックすると、新しいデバイスをペア設定できます"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> ボタン"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"戻る"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"上矢印"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"下矢印"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"左矢印"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"右矢印"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中央"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"タップすると詳細が表示されます"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"アラーム未設定"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"画面ロックを設定"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋認証センサー"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"認証"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"デバイスを入力"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折りたたんだ状態"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"広げた状態"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"バッテリー残量 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"タッチペンのバッテリー残量 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"タッチペンを充電器に接続してください"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"タッチペンのバッテリー残量が少なくなっています"</string>
     <string name="video_camera" msgid="7654002575156149298">"ビデオカメラ"</string>
@@ -1327,6 +1354,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string>
     <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index d367f4f..c21dc10 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"შეეხეთ სანახავად"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ეკრანის ჩანაწერის შენახვისას შეცდომა მოხდა"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ეკრანის ჩაწერის დაწყებისას წარმოიქმნა შეცდომა"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ჩანაწერის რეკორდერი"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"პრობლემის ჩანაწერის დამუშავება"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"მიმდინარე შეტყობინება პრობლემების შეგროვების სესიისთვის"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"პრობლემის ჩანაწერის შენახვისას წარმოიქმნა შეცდომა"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"პრობლემის ჩაწერის დაწყებისას წარმოიქმნა შეცდომა"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"მიმდინარეობს სრულ ეკრანზე ნახვა"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"გასვლისთვის გადაფურცლეთ ზემოდან ქვემოთ."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"გასაგებია"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"უკან"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"საწყისი"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"შენახული"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"კავშირის გაწყვეტა"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"გააქტიურება"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ისეთი ფუნქციები, როგორიცაა სწრაფი გაზიარება და ჩემი მოწყობილობის პოვნა, იყენებს Bluetooth-ს"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ჩაირთვება ხვალ დილით"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"აუდიოს გაზიარება"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"აუდიოს გაზიარება"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"თქვენი მოწყობილობის გამოცდილების რა ნაწილზე მოხდა ზეგავლენა?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"აირჩიეთ პრობლემის ტიპი"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ეკრანის ჩანაწერი"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"ეფექტურობა"</item>
-    <item msgid="1627504621139124393">"სამომხმარებლო ინტერფეისი"</item>
-    <item msgid="8309220355268900335">"ბატარეა"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"ეფექტურობა"</string>
+    <string name="user_interface" msgid="3712869377953950887">"სამომხმარებლო ინტერფეისი"</string>
+    <string name="thermal" msgid="6758074791325414831">"თერმული"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ცალი ხელის რეჟიმი"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"სმენის აპარატები"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"აქტიური"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"კავშირი გაწყვეტილია"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"სმენის აპარატები"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ახალი მოწყობილობის დაწყვილება"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"დააწკაპუნეთ ახალი მოწყობილობის დასაწყვილებლად"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"ავტოსუბტიტრები"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ღილაკი „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"უკან"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ზემოთ მიმართული ისარი"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ქვემოთ მიმართული ისარი"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"მარცხნივ მიმართული ისარი"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"მარჯვნივ მიმართული ისარი"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ცენტრში"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"შორისი"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"შეეხეთ მეტი ინფორმაციისთვის"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"მაღვიძარა არ არის"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ეკრანის დაბლოკვის შეყვანა"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"თითის ანაბეჭდის სენსორი"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ავტორიზაცია"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"მოწყობილობის შეყვანა"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"დაკეცილი"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"გაშლილი"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"დარჩენილია ბატარეის <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"სტილუსის ბატარეა <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"დააკავშირეთ თქვენი სტილუსი დამტენს"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"სტილუსის ბატარეა დაცლის პირასაა"</string>
     <string name="video_camera" msgid="7654002575156149298">"ვიდეოკამერა"</string>
@@ -1328,6 +1354,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string>
     <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"კლავიატურის შენათება"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"დონე: %1$d %2$d-დან"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"სახლის კონტროლი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index fd7160f..0a7b976 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Көру үшін түртіңіз."</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Экран жазбасын сақтау кезінде қате шықты."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Экрандағы бейнені жазу кезінде қате шықты."</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Мәселені жазу құралы"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Мәселе жазбасы өңделіп жатыр"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Мәселе туралы дерек жинау сеансына арналған ағымдағы хабарландыру"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Мәселе жазбасын сақтау кезінде қате шықты."</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Мәселені жазуды бастау кезінде қате шықты."</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Толық экранда көру"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Шығу үшін жоғарыдан төмен қарай сырғытыңыз."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Түсінікті"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Артқа"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Үй"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сақталды"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажырату"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"іске қосу"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share және Find My Device сияқты функциялар Bluetooth-ты пайдаланады."</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ертең таңертең қосылады."</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио бөлісу"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио бөлісу әрекеті орындалып жатыр."</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Құрылғы қызметінің қандай түріне әсер етті?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Мәселе түрін таңдаңыз."</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экранды жазу"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Өнімділік"</item>
-    <item msgid="1627504621139124393">"Пайдаланушы интерфейсі"</item>
-    <item msgid="8309220355268900335">"Батарея"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Өнімділік режимі"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Пайдаланушы интерфейсі"</string>
+    <string name="thermal" msgid="6758074791325414831">"Термовизия"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Есту құрылғылары"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Қосулы"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратулы"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Есту құрылғылары"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңа құрылғыны жұптау"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Параметрлер жинағын жаңарту мүмкін болмады."</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Параметрлер жинағы"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Құрылғы микрофонын блоктан шығару керек пе?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Құрылғы камерасын блоктан шығару керек пе?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Құрылғы камерасы мен микрофонын блоктан шығару керек пе?"</string>
@@ -597,7 +626,7 @@
     <string name="screen_pinning_exit" msgid="4553787518387346893">"Қолданба босатылды."</string>
     <string name="stream_voice_call" msgid="7468348170702375660">"Қоңырау шалу"</string>
     <string name="stream_system" msgid="7663148785370565134">"Жүйе"</string>
-    <string name="stream_ring" msgid="7550670036738697526">"Шылдырлау"</string>
+    <string name="stream_ring" msgid="7550670036738697526">"Шылдыр"</string>
     <string name="stream_music" msgid="2188224742361847580">"Мультимедиа"</string>
     <string name="stream_alarm" msgid="16058075093011694">"Дабыл"</string>
     <string name="stream_notification" msgid="7930294049046243939">"Хабарландыру"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> түймесі"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Артқа"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Жоғары бағыт пернесі"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Төмен бағыт пернесі"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Сол бағыт пернесі"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Оң бағыт пернесі"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Орталық"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Бос орын"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Толығырақ ақпарат алу үшін түртіңіз."</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Оятқыш орнатылмаған."</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"экран құлпын енгізу"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Саусақ ізін оқу сканері"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"аутентификациялау"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"құрылғыны енгізу"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"жабық"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ашық"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Қалған батарея заряды: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилус батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусты зарядтағышқа жалғаңыз."</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Стилус батареясының заряды аз"</string>
     <string name="video_camera" msgid="7654002575156149298">"Бейне­камера"</string>
@@ -1319,26 +1345,26 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Соңғы рет <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) қолданбасы пайдаланды."</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) қолданбасы пайдаланып жатыр"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Соңғы рет <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) қолданбасы пайдаланды."</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Жүйе"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мультитаскинг"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Кіріс"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Қолданба таңбашалары"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Арнайы мүмкіндіктер"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Перне тіркесімдері"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейверден қолдану"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейверден қолданыңыз."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 9adcfb5..20a9ae5 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ចុចដើម្បីមើល"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"មានបញ្ហាក្នុងការរក្សាទុក​ការថតវីដេអូអេក្រង់"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"មានបញ្ហា​ក្នុងការ​ចាប់ផ្ដើម​ថត​អេក្រង់"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"កម្មវិធីកត់ត្រាបញ្ហា"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"កំពុងដំណើរការការកត់ត្រាបញ្ហា"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ការជូនដំណឹងដែលកំពុងបន្តសម្រាប់វគ្គប្រមូលបញ្ហា"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"មានបញ្ហាក្នុងការរក្សាទុកកំណត់ត្រាបញ្ហា"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"មានបញ្ហាក្នុងការចាប់ផ្ដើមកត់ត្រាបញ្ហា"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"កំពុងមើលពេញអេក្រង់"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"ដើម្បីចាកចេញ សូមអូសពីលើចុះក្រោម។"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"យល់ហើយ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"ថយក្រោយ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"គេហ​ទំព័រ"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"បាន​រក្សាទុក"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ផ្ដាច់"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"បើកដំណើរការ"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"មុខងារដូចជា Quick Share និង \"រកឧបករណ៍របស់ខ្ញុំ\" ប្រើប៊្លូធូស"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ប៊្លូធូសនឹងបើកនៅព្រឹកស្អែក"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ការស្ដាប់សំឡេងរួមគ្នា"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"តើផ្នែកអ្វីនៃបទពិសោធប្រើប្រាស់ឧបករណ៍របស់អ្នកបានរងការប៉ះពាល់?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ជ្រើសរើសប្រភេទបញ្ហា"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ការថត​វីដេអូ​អេក្រង់"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"ប្រតិបត្តិការ"</item>
-    <item msgid="1627504621139124393">"ផ្ទៃប៉ះ"</item>
-    <item msgid="8309220355268900335">"ថ្ម"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"ប្រតិបត្តិការ"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ផ្ទៃប៉ះ"</string>
+    <string name="thermal" msgid="6758074791325414831">"កម្ដៅ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រើដៃម្ខាង"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"សកម្ម"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"បាន​ផ្ដាច់"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ផ្គូផ្គង​ឧបករណ៍ថ្មី"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ចុច ដើម្បីផ្គូផ្គងឧបករណ៍ថ្មី"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ប៊ូតុង <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ព្រួញ​ឡើង​លើ"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ព្រួញ​ចុះ​ក្រោម"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ព្រួញ​ទៅឆ្វេង"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ព្រួញទៅ​ស្ដាំ"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ចុចដើម្បីទទួលបាន​ព័ត៌មានបន្ថែម"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"មិនបាន​កំណត់​ម៉ោងរោទ៍​ទេ"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"បញ្ចូលព័ត៌មានផ្ទៀងផ្ទាត់សម្រាប់ការចាក់សោអេក្រង់"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ឧបករណ៍​ចាប់ស្នាមម្រាមដៃ"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ផ្ទៀងផ្ទាត់"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"បញ្ចូល​ឧបករណ៍"</string>
@@ -1201,7 +1228,7 @@
     <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិន​ភ្ជាប់ដោយស្វ័យ​ប្រវត្តិក្នុងពេលនេះទេ"</string>
     <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
     <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បី​ប្ដូរ​បណ្ដាញ សូមផ្ដាច់​អ៊ីសឺរណិត"</string>
-    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យ​បទពិសោធន៍ប្រើប្រាស់​ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្ម​នៅតែអាចស្កេនរក​បណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជា​នៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាន​នៅក្នុង​ការកំណត់​ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string>
+    <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យ​បទពិសោធប្រើប្រាស់​ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្ម​នៅតែអាចស្កេនរក​បណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជា​នៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាន​នៅក្នុង​ការកំណត់​ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string>
     <string name="turn_off_airplane_mode" msgid="8425587763226548579">"បិទមុខងារពេល​ជិះ​យន្តហោះ"</string>
     <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូល​ប្រអប់​ខាងក្រោម​ទៅក្នុង​ការកំណត់រហ័ស"</string>
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូល​ប្រអប់"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"បត់"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"លា"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ថ្មនៅសល់ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ថ្មប៊ិក <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ភ្ជាប់ប៊ិករបស់អ្នកជាមួយឆ្នាំងសាក"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ថ្មប៊ិកនៅសល់តិច"</string>
     <string name="video_camera" msgid="7654002575156149298">"កាមេរ៉ា​វីដេអូ"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"បានប្រើនាពេលថ្មីៗនេះដោយ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"កំពុងប្រើដោយ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"បានប្រើនាពេលថ្មីៗនេះដោយ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"ប្រព័ន្ធ"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ការដំណើរការបានច្រើន"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ធាតុចូល"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ផ្លូវកាត់​កម្មវិធី"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ភាពងាយស្រួល"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"ផ្លូវកាត់​ក្ដារ​ចុច"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ផ្លូវ​កាត់ការស្វែងរក"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index ecc0fdf..922075a 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೇವ್‌ ಮಾಡುವಾಗ ದೋಷ ಎದುರಾಗಿದೆ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಾರಂಭಿಸುವಾಗ ದೋಷ ಕಂಡುಬಂದಿದೆ"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ಸಮಸ್ಯೆ ರೆಕಾರ್ಡರ್"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ಸಮಸ್ಯೆ ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಕ್ರಿಯೆ…"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ಸಮಸ್ಯೆ ಸಂಗ್ರಹಣೆಯ ಸೆಶನ್‌ಗಾಗಿ ಜರುಗುತ್ತಿರುವ ನೋಟಿಫಿಕೇಶನ್"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ಸಮಸ್ಯೆಯ ರೆಕಾರ್ಡಿಂಗ್ ಅನ್ನು ಸೇವ್‌ ಮಾಡುವಾಗ ದೋಷ ಎದುರಾಗಿದೆ"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ಸಮಸ್ಯೆಯ ರೆಕಾರ್ಡಿಂಗ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸುವಲ್ಲಿ ದೋಷ ಎದುರಾಗಿದೆ"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"ನಿರ್ಗಮಿಸಲು, ಮೇಲಿನಿಂದ ಕೆಳಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"ಅರ್ಥವಾಯಿತು"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"ಹಿಂದೆ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"ಮುಖಪುಟ"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ಡಿಸ್‌ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ಕ್ವಿಕ್ ಶೇರ್ ಮತ್ತು Find My Device ನಂತಹ ಫೀಚರ್‌ಗಳು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್‌ಸೆಟ್"</string>
@@ -361,14 +391,14 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ಸಾಧನ ಬಳಸುವಾಗ ನೀವು ಯಾವ ರೀತಿಯ ಸಮಸ್ಯೆ ಎದುರಿಸುತ್ತೀರಿ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ಸಮಸ್ಯೆಯ ಪ್ರಕಾರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"ಕಾರ್ಯಕ್ಷಮತೆ"</item>
-    <item msgid="1627504621139124393">"ಬಳಕೆದಾರರ ಇಂಟರ್‌ಫೇಸ್"</item>
-    <item msgid="8309220355268900335">"ಬ್ಯಾಟರಿ"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"ಪರ್ಫಾರ್ಮೆನ್ಸ್"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ಬಳಕೆದಾರ ಇಂಟರ್‌ಫೇಸ್"</string>
+    <string name="thermal" msgid="6758074791325414831">"ಥರ್ಮಲ್"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ಒಂದು ಕೈ ಮೋಡ್"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
-    <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ಸಕ್ರಿಯವಾಗಿದೆ"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ಡಿಸ್‌ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
+    <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> ಬಟನ್"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"ಹಿಂದೆ"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ಅಪ್ ಆ್ಯರೋ"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ಡೌನ್ ಆ್ಯರೋ"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ಲೆಫ್ಟ್ ಆ್ಯರೋ"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ರೈಟ್ ಆ್ಯರೋ"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ಮಧ್ಯ"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"ಸ್ಪೇಸ್"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ಅಲಾರಾಂ ಸೆಟ್ ಆಗಿಲ್ಲ"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ನಮೂದಿಸಿ"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ದೃಢೀಕರಿಸಿ"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ಸಾಧನವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ಅನ್‌ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ ಉಳಿದಿದೆ"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ನಿಮ್ಮ ಸ್ಟೈಲಸ್ ಅನ್ನು ಚಾರ್ಜರ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಿದೆ"</string>
     <string name="video_camera" msgid="7654002575156149298">"ವೀಡಿಯೊ ಕ್ಯಾಮರಾ"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_NAME">%1$s</xliff:g> ಇದನ್ನು ಬಳಸಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಂದ ಬಳಕೆಯಲ್ಲಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_NAME">%1$s</xliff:g> ಇದನ್ನು ಬಳಸಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"ಸಿಸ್ಟಂ"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ಮಲ್ಟಿಟಾಸ್ಕಿಂಗ್"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ಇನ್‌ಪುಟ್"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ಆ್ಯಪ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್‌ಲೈಟ್"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 889026e..496307a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"탭하여 보기"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"화면 녹화 저장 중에 오류가 발생했습니다."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"화면 녹화 시작 중 오류 발생"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"문제 녹화 도구"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"문제 녹화 처리 중"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"문제 수집 섹션에 대한 진행 중 알림"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"문제 녹화 저장 중에 오류가 발생했습니다."</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"문제 녹화 시작 중에 오류가 발생했습니다."</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"전체 화면 모드"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"종료하려면 위에서 아래로 스와이프합니다."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"확인"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"뒤로"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"홈"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"저장됨"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"연결 해제"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"실행"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, 내 기기 찾기 등의 기능에서 블루투스를 사용합니다."</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"블루투스가 내일 아침에 켜집니다."</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"오디오 공유"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"오디오 공유 중"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"기기 경험의 어떤 부분에 영향이 있었나요?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"문제 유형 선택"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"화면 녹화"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"성능"</item>
-    <item msgid="1627504621139124393">"사용자 인터페이스"</item>
-    <item msgid="8309220355268900335">"배터리"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"성능"</string>
+    <string name="user_interface" msgid="3712869377953950887">"사용자 인터페이스"</string>
+    <string name="thermal" msgid="6758074791325414831">"열화상"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 사용 모드"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"청각 보조 기기"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"활성"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"연결 끊김"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"청각 보조 기기"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"새 기기와 페어링"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"새 기기와 페어링하려면 클릭하세요"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 버튼"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"뒤로"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"위쪽 화살표"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"아래쪽 화살표"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"왼쪽 화살표"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"오른쪽 화살표"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"중앙"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"탭하여 자세한 정보를 확인하세요."</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"설정된 알람 없음"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"화면 잠금 입력"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"지문 센서"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"인증"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"기기 입력"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"접은 상태"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"펼친 상태"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"배터리 <xliff:g id="PERCENTAGE">%s</xliff:g> 남음"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"스타일러스 배터리 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"스타일러스를 충전기에 연결하세요"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"스타일러스 배터리 부족"</string>
     <string name="video_camera" msgid="7654002575156149298">"비디오 카메라"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"최근 <xliff:g id="APP_NAME">%1$s</xliff:g>에서 사용됨(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 사용 중(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"최근 <xliff:g id="APP_NAME">%1$s</xliff:g>에서 사용됨(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"시스템"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"멀티태스킹"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"입력"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"앱 바로가기"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"접근성"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 0ec1558..2e62142 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Көрүү үчүн таптаңыз"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Экран тартылган жок"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Экранды жаздырууну баштоодо ката кетти"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Маселе жаздыргыч"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Маселе жаздырылууда"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Маселе тууралуу маалымат чогултулуп жатканы жөнүндө учурдагы билдирме"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Жаздырылган маселе сакталган жок"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Маселени жаздыруу башталбай койду"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Толук экран режимин көрүү"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Чыгуу үчүн экранды ылдый сүрүп коюңуз."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Түшүндүм"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Артка"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Үйгө"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сакталды"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажыратуу"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"иштетүү"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth Тез бөлүшүү жана Түзмөгүм кайда? сыяктуу функцияларда колдонулат"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Чогуу угуу"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Чогуу угулууда"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Түзмөгүңүздүн кайсы бөлүгүнө кедергиси тийди?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Маселенин түрүн тандоо"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экрандан видео жаздырып алуу"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Иштин майнаптуулугу"</item>
-    <item msgid="1627504621139124393">"Колдонуучунун интерфейси"</item>
-    <item msgid="8309220355268900335">"Батарея"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Иштин майнаптуулугу"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Колдонуучунун интерфейси"</string>
+    <string name="thermal" msgid="6758074791325414831">"Жылуулук"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Угуу аппараттары"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Жигердүү"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратылды"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Угуу аппараттары"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңы түзмөк кошуу"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңы түзмөк кошуу үчүн басыңыз"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Алдын ала коюлган параметрлер жаңыртылган жок"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Алдын ала коюлган параметрлер"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Ыкчам коштомо жазуулар"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Түзмөктүн микрофонун бөгөттөн чыгарасызбы?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Түзмөктүн камерасын бөгөттөн чыгарасызбы?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Түзмөктүн камерасы менен микрофону бөгөттөн чыгарылсынбы?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> баскычы"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Башкы бет"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Артка"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Өйдө жебе"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Ылдый жебе"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Солго жебе"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Оңго жебе"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Ортолотуу"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Боштук"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Кеңири маалымат алуу үчүн таптап коюңуз"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ойготкуч коюлган жок"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"экран кулпусун киргизүү"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Манжа изинин сенсору"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"аныктыгын текшерүү"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"түзмөккө кирүү"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"бүктөлгөн"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ачылган"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Батареянын кубаты: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилустун батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусту кубаттаңыз"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Стилустун батареясы отурайын деп калды"</string>
     <string name="video_camera" msgid="7654002575156149298">"Видео камера"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Акыркы жолу <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) колдонмосунда иштетилди"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда иштеп жатат (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Акыркы жолу <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) колдонмосунда иштетилди"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Система"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Бир нече тапшырма аткаруу"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Киргизүү"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Колдонмодогу кыска жолдор"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Атайын мүмкүнчүлүктөр"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Ыкчам баскычтар"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index aea79e8..235015b 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -34,7 +34,7 @@
     <dimen name="volume_row_slider_height">128dp</dimen>
 
     <!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
-    <dimen name="immersive_mode_cling_width">380dp</dimen>
+    <dimen name="immersive_mode_cling_width">500dp</dimen>
 
     <dimen name="controls_activity_view_top_offset">25dp</dimen>
 
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 6ba0b09..fb3b377 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ແຕະເພື່ອເບິ່ງ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ເກີດຂໍ້ຜິດພາດໃນການບັນທຶກໜ້າຈໍ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ເກີດຄວາມຜິດພາດໃນການບັນທຶກໜ້າຈໍ"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ຕົວບັນທຶກບັນຫາ"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ກຳລັງປະມວນຜົນການບັນທຶກບັນຫາ"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ການແຈ້ງເຕືອນທີ່ດຳເນີນຢູ່ສຳລັບເຊດຊັນການຮວບຮວມບັນຫາ"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ເກີດຂໍ້ຜິດພາດໃນການບັນທຶກບັນຫາ"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ເກີດຂໍ້ຜິດພາດໃນການເລີ່ມຕົ້ນການບັນທຶກບັນຫາ"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"ກຳລັງ​ເບິ່ງ​ເຕັມ​​ຈໍ"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"ເພື່ອອອກ, ໃຫ້ປັດລົງຈາກເທິງສຸດ."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"ເຂົ້າໃຈແລ້ວ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"ກັບຄືນ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"ໜ້າທຳອິດ"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ບັນທຶກແລ້ວ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ຕັດການເຊື່ອມຕໍ່"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ເປີດນຳໃຊ້"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ເປີດໃຊ້ໂດຍອັດຕະໂນມັດອີກຄັ້ງມື້ອື່ນ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ຄຸນສົມບັດຕ່າງໆໃຊ້ Bluetooth ເຊັ່ນ: ການແຊຣ໌ດ່ວນ ແລະ ຊອກຫາອຸປະກອນຂອງຂ້ອຍ"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ຈະເປີດມື້ອື່ນເຊົ້າ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ການແບ່ງປັນສຽງ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ກຳລັງແບ່ງປັນສຽງ"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ສ່ວນໃດຂອງປະສົບການອຸປະກອນຂອງທ່ານໄດ້ຮັບຜົນກະທົບ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ເລືອກປະເພດບັນຫາ"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ບັນທຶກໜ້າຈໍ"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"ປະສິດທິພາບ"</item>
-    <item msgid="1627504621139124393">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</item>
-    <item msgid="8309220355268900335">"ແບັດເຕີຣີ"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"ປະສິດທິພາບ"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</string>
+    <string name="thermal" msgid="6758074791325414831">"ຄວາມຮ້ອນ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝດມືດຽວ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ອຸປະກອນຊ່ວຍຟັງ"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ນຳໃຊ້ຢູ່"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ອຸປະກອນຊ່ວຍຟັງ"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ຄລິກເພື່ອຈັບຄູ່ອຸປະກອນໃໝ່"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ບໍ່ສາມາດອັບເດດການຕັ້ງຄ່າລ່ວງໜ້າໄດ້"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ຄ່າທີ່ກຳນົດລ່ວງໜ້າ"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"ຄຳບັນຍາຍສົດ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ປົດບລັອກໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ປົດບລັອກກ້ອງຖ່າຍຮູບອຸ​ປະ​ກອນບໍ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ຍົກເລີກການບລັອກກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນອຸ​ປະ​ກອນບໍ?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ປຸ່ມ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"ກັບຄືນ"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ລູກສອນຂຶ້ນ"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ລູກສອນລົງ"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ລູກສອນຊ້າຍ"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ລູກສອນຂວາ"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ເຄິ່ງກາງ"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ແຕະເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ບໍ່ໄດ້ຕັ້ງໂມງປຸກ"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ໃສ່ຂໍ້ມູນການລັອກໜ້າຈໍ"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ເຊັນ​ເຊີລາຍນິ້ວ​ມື"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ພິສູດຢືນຢັນ"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ເຂົ້າອຸປະກອນ"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ພັບແລ້ວ"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ກາງອອກແລ້ວ"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ແບັດເຕີຣີເຫຼືອ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ແບັດເຕີຣີປາກກາ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ເຊື່ອມຕໍ່ປາກກາຂອງທ່ານກັບສາຍສາກ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ແບັດເຕີຣີປາກກາເຫຼືອໜ້ອຍ"</string>
     <string name="video_camera" msgid="7654002575156149298">"ກ້ອງວິ​ດີ​ໂອ"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ໃຊ້ຫຼ້າສຸດໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"ໃຊ້ຢູ່ໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ໃຊ້ຫຼ້າສຸດໂດຍ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"ລະບົບ"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ການເຮັດຫຼາຍໜ້າວຽກພ້ອມກັນ"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ອິນພຸດ"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ທາງລັດແອັບ"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ການຊ່ວຍເຂົ້າເຖິງ"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"ຄີລັດ"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ໄຟປຸ່ມແປ້ນພິມ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ລະດັບທີ %1$d ຈາກ %2$d"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 40418f0..cca7b7f 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Palieskite, kad peržiūrėtumėte"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Išsaugant ekrano įrašą įvyko klaida"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Pradedant ekrano vaizdo įrašymą iškilo problema"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Problemų įrašytuvas"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Apdorojamas problemos įrašas"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Šiuo metu rodomas problemos duomenų rinkimo seanso pranešimas"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Išsaugant problemos įrašą įvyko klaida"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Pradedant problemos įrašą įvyko klaida"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Peržiūrima viso ekrano režimu"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Jei norite išeiti, perbraukite žemyn iš viršaus."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Supratau"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Atgal"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Pagrindinis"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Išsaugota"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atjungti"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"suaktyvinti"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatiškai vėl įjungti rytoj"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tokioms funkcijoms kaip „Spartusis bendrinimas“ ir „Rasti įrenginį“ naudojamas „Bluetooth“ ryšys"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"„Bluetooth“ ryšys bus įjungtas rytoj ryte"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Garso įrašų bendrinimas"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Bendrinamas garso įrašas"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Bendrinti garsą"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Bendrinamas garsas"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri įrenginio funkcija buvo paveikta?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano įrašas"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Našumas"</item>
-    <item msgid="1627504621139124393">"Naudotojo sąsaja"</item>
-    <item msgid="8309220355268900335">"Akumuliatorius"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Našumas"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Naudotojo sąsaja"</string>
+    <string name="thermal" msgid="6758074791325414831">"Šiluminis"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos režimas"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Klausos įrenginiai"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktyvus"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atjungtas"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Klausos įrenginiai"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Susieti naują įrenginį"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Išankstinių nustatymų atnaujinti nepavyko"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Išankstiniai nustatymai"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Subtitrai realiuoju laiku"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Panaikinti įrenginio mikrofono blokavimą?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Panaikinti įrenginio fotoaparato blokavimą?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Panaikinti įrenginio fotoaparato ir mikrofono blokavimą?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Mygtukas <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Pagrindinis"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Atgal"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Rodyklė aukštyn"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Rodyklė žemyn"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Rodyklė kairėn"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Rodyklė dešinėn"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centras"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Tarpas"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Įvesti"</string>
@@ -1179,6 +1201,8 @@
     <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>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <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>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sulenkta"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"nesulenkta"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Liko akumuliatoriaus įkrovos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Liko rašiklio akumuliatoriaus energijos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Prijunkite rašiklį prie kroviklio"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Senka rašiklio akumuliatorius"</string>
     <string name="video_camera" msgid="7654002575156149298">"Vaizdo kamera"</string>
@@ -1328,6 +1352,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string>
     <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 53c5408..7c72c7e 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Pieskarieties, lai skatītu"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Saglabājot ekrāna ierakstu, radās kļūda."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Sākot ierakstīt ekrāna saturu, radās kļūda."</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Problēmu ierakstītājs"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Apstrādā problēmas ierakstu"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Aktīvs paziņojums par problēmu vākšanas sesiju"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Saglabājot problēmas ierakstu, radās kļūda."</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Sākot problēmas ierakstīšanu, radās kļūda."</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Skatīšanās pilnekrāna režīmā"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Lai izietu, no augšdaļas velciet lejup."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Labi"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Atpakaļ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Sākums"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atvienot"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizēt"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tādas funkcijas kā “Ātrā kopīgošana” un “Atrast ierīci” izmanto Bluetooth savienojumu"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth savienojums tiks ieslēgts rīt no rīta"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kopīgot audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Notiek audio kopīgošana"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstīšana"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Veiktspēja"</item>
-    <item msgid="1627504621139124393">"Lietotāja saskarne"</item>
-    <item msgid="8309220355268900335">"Akumulators"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Veiktspēja"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Lietotāja saskarne"</string>
+    <string name="thermal" msgid="6758074791325414831">"Ierīces temperatūra"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas režīms"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dzirdes aparāti"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktīvs"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atvienots"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dzirdes aparāti"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienot pārī jaunu ierīci"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pirmsiestatījums"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Subtitri reāllaikā"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vai atbloķēt ierīces mikrofonu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vai vēlaties atbloķēt ierīces kameru?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vai atbloķēt ierīces kameru un mikrofonu?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Poga <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Sākumvietas taustiņš"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Atpakaļ"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Augšupvērstā bultiņa"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Lejupvērstā bultiņa"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Kreisā bultiņa"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Labā bultiņa"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrā"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Atstarpe"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Ievadīšanas taustiņš"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Pieskarieties, lai iegūtu plašāku informāciju."</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nav iestatīts signāls"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ievadīt ekrāna bloķēšanas informāciju"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Pirksta nospieduma sensors"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"veiktu autentificēšanu"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"izmantotu ierīci"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"aizvērta"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"atvērta"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Atlikušais uzlādes līmenis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Skārienekrāna pildspalvas akumulatora uzlādes līmenis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pievienojiet skārienekrāna pildspalvu lādētājam"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Zems skārienekrāna pildspalvas akumulatora līmenis"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nesen to izmantoja lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"To izmanto lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nesen to izmantoja lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistēma"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Vairākuzdevumu režīms"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ievade"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lietotņu saīsnes"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pieejamība"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 8eb0537..a8baf35 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Допрете за прегледување"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Грешка при зачувувањето на снимката од екранот"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Грешка при почетокот на снимањето на екранот"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Проблем со „Диктафон“"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Се обработува проб. со снимање"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Тековно известување за сесија за проблем со прибирање"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Грешка при зачувување на проблемот со снимање"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Грешка при започнување на проблемот со снимање"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Се прикажува на цел екран"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"За да излезете, повлечете одозгора надолу."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Сфатив"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Назад"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Почетна страница"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Зачувано"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекини врска"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирај"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функциите како „Брзо споделување“ и „Најди го мојот уред“ користат Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ќе се вклучи утре наутро"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделување аудио"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Се споделува аудио"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string>
@@ -301,7 +331,7 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string>
-    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Мрежите се достапни"</string>
+    <string name="quick_settings_networks_available" msgid="1875138606855420438">"Достапни се мрежи"</string>
     <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Не се достапни мрежи"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Нема достапни Wi-Fi мрежи"</string>
     <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Се вклучува…"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Кој дел од доживувањето на уредот беше засегнат?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екран"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Изведба"</item>
-    <item msgid="1627504621139124393">"Кориснички интерфејс"</item>
-    <item msgid="8309220355268900335">"Батерија"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Изведба"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string>
+    <string name="thermal" msgid="6758074791325414831">"Термално"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим со една рака"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не е поврзано"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спари нов уред"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не можеше да се ажурира зададената вредност"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Зададени вредности"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Автоматски титлови"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се одблокира пристапот до микрофонот на уредот?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се одблокира пристапот до камерата на уредот?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се одблокира пристапот до камерата и микрофонот на уредот?"</string>
@@ -685,8 +714,8 @@
     <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string>
     <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string>
-    <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или вибрира во зависност од поставките за уредот"</string>
-    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или вибрира во зависност од поставките на уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
+    <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или да вибрира во зависност од поставките за уредот"</string>
+    <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или да вибрира во зависност од поставките за уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string>
     <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string>
     <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"&lt;b&gt;Статус:&lt;/b&gt; поставено на „Стандардно“"</string>
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Статус:&lt;/b&gt; намалено на „Тивко“"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Копче <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home-копче"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка нагоре"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка надолу"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка налево"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка надесно"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центар"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -989,7 +1013,7 @@
     <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затворете ги поставките за зголемување"</string>
     <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Излегување од „Режим на изменување“"</string>
     <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Повлечете на аголот за да ја промените големината"</string>
-    <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволете дијагонално лизгање"</string>
+    <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволи дијагонално лизгање"</string>
     <string name="accessibility_resize" msgid="5733759136600611551">"Промени големина"</string>
     <string name="accessibility_change_magnification_type" msgid="666000085077432421">"Променете го типот на зголемување"</string>
     <string name="accessibility_magnification_end_resizing" msgid="4881690585800302628">"Заврши ја промената на големина"</string>
@@ -998,7 +1022,7 @@
     <string name="accessibility_magnification_right_handle" msgid="9055988237319397605">"Десна рачка"</string>
     <string name="accessibility_magnification_bottom_handle" msgid="6531646968813821258">"Долна рачка"</string>
     <string name="accessibility_magnification_settings_panel_description" msgid="8174187340747846953">"Поставки за зголемување"</string>
-    <string name="accessibility_magnifier_size" msgid="3038755600030422334">"Големина на лупа"</string>
+    <string name="accessibility_magnifier_size" msgid="3038755600030422334">"Големина на лупата"</string>
     <string name="accessibility_magnification_zoom" msgid="4222088982642063979">"Зум"</string>
     <string name="accessibility_magnification_medium" msgid="6994632616884562625">"Средно"</string>
     <string name="accessibility_magnification_small" msgid="8144502090651099970">"Мало"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Допрете за повеќе информации"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Не е поставен аларм"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"внесете PIN/шема/лозинка"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отпечатоци"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"автентицирате"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"внесете уред"</string>
@@ -1211,7 +1237,7 @@
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Активна е # апликација}one{Активни се # апликација}other{Активни се # апликации}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Нови информации"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни апликации"</string>
-    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат, дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може да влијае и на траењето на батеријата."</string>
+    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може и да влијае на траењето на батеријата."</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Запри"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Запрено"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Готово"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворен"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворен"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Преостаната батерија: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерија на пенкало: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поврзете го пенкалото со полнач"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string>
     <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Неодамна користено од <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Се користи од <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Неодамна користено од <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Систем"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Мултитаскинг"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Внесување"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Кратенки за апликации"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Пристапност"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Кратенки од тастатура"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 291ee6a..9f19680 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"കാണാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"സ്ക്രീൻ റെക്കോർഡിംഗ് സംരക്ഷിക്കുന്നതിൽ പിശക്"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"സ്ക്രീൻ റെക്കോർഡിംഗ് ആരംഭിക്കുന്നതിൽ പിശക്"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"പ്രശ്‌ന റെക്കോർഡർ"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"പ്രശ്‌ന റെക്കോർഡിംഗ് പ്രോസസ് ചെയ്യുന്നു"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ഒരു പ്രശ്‌ന ശേഖരണ സെഷന്റെ ഓൺ‌ഗോയിംഗ് അറിയിപ്പ്"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"പ്രശ്‌ന റെക്കോർഡിംഗ് സംരക്ഷിക്കുന്നതിൽ പിശക്"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"പ്രശ്‌ന റെക്കോർഡിംഗ് ആരംഭിക്കുന്നതിൽ പിശക്"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"പൂർണ്ണ സ്‌ക്രീനിൽ കാണുന്നു"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"പുറത്തുകടക്കാൻ, മുകളിൽ നിന്ന് താഴോട്ട് സ്വൈപ്പ് ചെയ്യുക."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"മനസ്സിലായി"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"മടങ്ങുക"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"ഹോം"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"സംരക്ഷിച്ചു"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"വിച്ഛേദിക്കുക"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"സജീവമാക്കുക"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"നാളെ വീണ്ടും സ്വയമേവ ഓണാക്കുക"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ക്വിക്ക് ഷെയർ, Find My Device പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth നാളെ രാവിലെ ഓണാകും"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ഓഡിയോ പങ്കിടൽ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ഓഡിയോ പങ്കിടുന്നു"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്‌സെറ്റ്"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"നിങ്ങളുടെ ഉപകരണ അനുഭവത്തിന്‍റെ ഏത് ഭാഗമാണ് ബാധിച്ചത്?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"പ്രശ്ന തരം തിരഞ്ഞെടുക്കുക"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"സ്‌ക്രീൻ റെക്കോർഡ്"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"പ്രകടനം"</item>
-    <item msgid="1627504621139124393">"ഉപയോക്തൃ ഇന്‍റര്‍ഫേസ്"</item>
-    <item msgid="8309220355268900335">"ബാറ്ററി"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"പ്രകടനം"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ഉപയോക്തൃ ഇന്റർഫേസ്"</string>
+    <string name="thermal" msgid="6758074791325414831">"തെർമൽ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ഒറ്റക്കൈ മോഡ്"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"സജീവം"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"വിച്ഛേദിച്ചിരിക്കുന്നു"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"പുതിയ ഉപകരണം ജോടിയാക്കുക"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"പുതിയ ഉപകരണം ജോടിയാക്കാൻ ക്ലിക്ക് ചെയ്യുക"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"പ്രീസെറ്റ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"പ്രീസെറ്റ്"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"തത്സമയ ക്യാപ്‌ഷനുകൾ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ഉപകരണ ക്യാമറയോ മൈക്രോഫോണോ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ബട്ടൺ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"ഹോം"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"ബാക്ക്"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"മുകളിലേക്കുള്ള അമ്പടയാളം"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"താഴേക്കുള്ള അമ്പടയാളം"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ഇടത് അമ്പടയാളം"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"വലത് അമ്പടയാളം"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"മധ്യം"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"TAB"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"സ്പെയ്സ്"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"എന്റർ"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"കൂടുതൽ വിവരങ്ങൾക്ക് ടാപ്പ് ചെയ്യുക"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"അലാറം സജ്ജീകരിച്ചിട്ടില്ല"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"സ്‌ക്രീൻ ലോക്ക് നൽകുക"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ഫിംഗർപ്രിന്റ് സെൻസർ"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"പരിശോധിച്ചുറപ്പിക്കുക"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ഉപകരണം നൽകുക"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ഫോൾഡ് ചെയ്തത്"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"അൺഫോൾഡ് ചെയ്തത്"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ബാറ്ററി ചാർജ് ശേഷിക്കുന്നു"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"സ്റ്റൈലസ് ബാറ്ററി <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"നിങ്ങളുടെ സ്റ്റൈലസ് ചാർജറുമായി കണക്റ്റ് ചെയ്യുക"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"സ്റ്റൈലസിന്റെ ബാറ്ററി ചാർജ് കുറവാണ്"</string>
     <string name="video_camera" msgid="7654002575156149298">"വീഡിയോ ക്യാമറ"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) അടുത്തിടെ ഉപയോഗിച്ചു"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ഉപയോഗിക്കുന്നു"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) അടുത്തിടെ ഉപയോഗിച്ചു"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"സിസ്റ്റം"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"മൾട്ടിടാസ്‌കിംഗ്"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ഇൻപുട്ട്"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ആപ്പ് കുറുക്കുവഴികൾ"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ഉപയോഗസഹായി"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"കീബോഡ് കുറുക്കുവഴികൾ"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്‌ലൈറ്റ്"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index da8db8d..a6ab892 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Харахын тулд товшино уу"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Дэлгэцийн бичлэгийг хадгалахад алдаа гарлаа"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Дэлгэцийн бичлэгийг эхлүүлэхэд алдаа гарлаа"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Асуудал бичигч"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Асуудлын бичлэгийг боловсруулж байна"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Асуудал цуглуулах харилцан үйлдлийн үргэлжилж буй мэдэгдэл"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Асуудлын бичлэгийг хадгалахад алдаа гарлаа"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Асуудлын бичлэгийг эхлүүлэхэд алдаа гарлаа"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Бүтэн дэлгэцээр үзэж байна"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Гарах бол дээрээс доош шударна уу."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Ойлголоо"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Буцах"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Гэрийн"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Хадгалсан"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"салгах"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"идэвхжүүлэх"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд Bluetooth-г ашигладаг"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-г маргааш өглөө асаана"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио хуваалцах"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио хуваалцаж байна"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Аудио хуваалцах"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Аудио хуваалцаж байна"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Таны төхөөрөмжийн хэрэглээний аль хэсэгт нөлөөлсөн бэ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Асуудлын төрөл сонгоно уу"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Дэлгэцийн бичлэг"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Гүйцэтгэл"</item>
-    <item msgid="1627504621139124393">"Хэрэглэгчийн интерфейс"</item>
-    <item msgid="8309220355268900335">"Батарей"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Гүйцэтгэл"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Хэрэглэгчийн интерфейс"</string>
+    <string name="thermal" msgid="6758074791325414831">"Дулааны"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг гарын горим"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Сонсголын төхөөрөмжүүд"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Идэвхтэй"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Салсан"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Сонсголын төхөөрөмжүүд"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Шинэ төхөөрөмж хослуулах"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Шинэ төхөөрөмж хослуулахын тулд товшино уу"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Урьдчилсан тохируулгыг шинэчилж чадсангүй"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Урьдчилсан тохируулга"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Шууд тайлбар"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Төхөөрөмжийн микрофоныг блокоос гаргах уу?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Төхөөрөмжийн камерыг блокоос гаргах уу?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Төхөөрөмжийн камер болон микрофоныг блокоос гаргах уу?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> товчлуур"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Нүүр хуудас"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Буцах"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Дээш сум"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Доош сум"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Зүүн сум"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Баруун сум"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Гол хэсэг"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Зай"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Оруулах"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Нэмэлт мэдээлэл авахын тулд товшино уу"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Сэрүүлэг тавиагүй"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"дэлгэцийн түгжээ оруулах"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Хурууны хээ мэдрэгч"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"баталгаажуулах"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"төхөөрөмж оруулах"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"эвхсэн"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"дэлгэсэн"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> батарей үлдлээ"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Мэдрэгч үзгийн батарей <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Мэдрэгч үзгээ цэнэглэгчтэй холбоорой"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Мэдрэгч үзэгний батарей бага байна"</string>
     <string name="video_camera" msgid="7654002575156149298">"Видео камер"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Саяхан <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ашигласан"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ашиглаж байна"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Саяхан <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ашигласан"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Систем"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Олон ажил зэрэг хийх"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Оролт"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Аппын товчлол"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Хандалт"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Товчлуурын шууд холбоос"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 7dd9308..cc6e59a 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"पाहण्यासाठी टॅप करा"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"स्क्रीन रेकॉर्डिंग सेव्ह करताना एरर आली"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन रेकॉर्डिंग सुरू करताना एरर आली"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"समस्या रेकॉर्डर"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"प्रक्रियेच्या समस्येचे रेकॉर्डिंग"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"समस्या गोळा करण्याच्या सेशनसाठीची सद्य सूचना"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"समस्येचे रेकॉर्डिंग सेव्ह करताना एरर आली"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"समस्येचे रेकॉर्डिंग सुरू करताना एरर आली"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"पूर्ण स्क्रीन पाहत आहात"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"बाहेर पडण्यासाठी, वरून खाली स्वाइप करा."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"समजले"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"मागे"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"होम"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव्ह केले"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट करा"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ॲक्टिव्हेट करा"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेअर आणि Find My Device यांसारखी वैशिष्‍ट्ये ब्लूटूथ वापरतात"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ उद्या सकाळी सुरू होईल"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडिओ शेअरिंग"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडिओ शेअर करत आहे"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ऑडिओ शेअर करा"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ऑडिओ शेअर करत आहे"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तुमच्या डिव्हाइसबाबत कोणत्या अनुभवावर परिणाम झाला?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्येचा प्रकार निवडा"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रेकॉर्ड"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"परफॉर्मन्स"</item>
-    <item msgid="1627504621139124393">"यूझर इंटरफेस"</item>
-    <item msgid="8309220355268900335">"बॅटरी"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"परफॉर्मन्स"</string>
+    <string name="user_interface" msgid="3712869377953950887">"यूझर इंटरफेस"</string>
+    <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ॲक्टिव्ह आहे"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट केले आहे"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"श्रवणयंत्रे"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नवीन डिव्हाइस पेअर करा"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"बटण <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"परत"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप अ‍ॅरो"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन अ‍ॅरो"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ्ट अ‍ॅरो"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट अ‍ॅरो"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"मध्यवर्ती"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"अधिक माहितीसाठी टॅप करा"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"अलार्म सेट केला नाही"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"स्क्रीन लॉक एंटर करा"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फिंगरप्रिंट सेन्सर"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ऑथेंटिकेट करा"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिव्हाइस एंटर करा"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड केलेले"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"फोल्ड न केलेले"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> बॅटरी शिल्लक आहे"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टायलस बॅटरी <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"तुमचे स्टायलस चार्जरशी कनेक्ट करा"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"स्टायलस बॅटरी कमी आहे"</string>
     <string name="video_camera" msgid="7654002575156149298">"व्हिडिओ कॅमेरा"</string>
@@ -1327,6 +1352,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string>
     <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index abd9d01..ced6541 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Ketik untuk lihat"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Ralat semasa menyimpan rakaman skrin"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ralat semasa memulakan rakaman skrin"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Perakam Masalah"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Memproses rakaman masalah"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Pemberitahuan sedang berlangsung untuk sesi pengumpulan masalah"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Ralat menyimpan rakaman masalah"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Ralat memulakan rakaman masalah"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Melihat skrin penuh"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Untuk keluar, leret ke bawah dari bahagian atas."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Kembali"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Rumah"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan sambungan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan lagi esok secara automatik"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Ciri seperti Quick Share dan Find My Device menggunakan Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dihidupkan esok pagi"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Perkongsian Audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berkongsi Audio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Prestasi"</item>
-    <item msgid="1627504621139124393">"Antara Muka Pengguna"</item>
-    <item msgid="8309220355268900335">"Bateri"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Prestasi"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Antara Muka Pengguna"</string>
+    <string name="thermal" msgid="6758074791325414831">"Terma"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Peranti pendengaran"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Diputuskan sambungan"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Peranti pendengaran"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Gandingkan peranti baharu"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string>
@@ -377,8 +407,8 @@
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Nyahsekat mikrofon peranti?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Nyahsekat kamera peranti?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Nyahsekat kamera dan mikrofon peranti?"</string>
-    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan mikrofon anda."</string>
-    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera anda."</string>
+    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan mikrofon."</string>
+    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Tindakan ini menyahsekat akses bagi semua apl dan perkhidmatan yang dibenarkan untuk menggunakan kamera atau mikrofon anda."</string>
     <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"Mikrofon disekat"</string>
     <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"Kamera disekat"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Butang <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Skrin Utama"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Kembali"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Anak panah ke atas"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Anak panah ke bawah"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Anak panah ke kiri"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Anak panah ke kanan"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Tengah"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -933,7 +958,7 @@
     <string name="running_foreground_services_title" msgid="5137313173431186685">"Apl yang berjalan di latar belakang"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"Ketik untuk mendapatkan butiran tentang penggunaan kuasa bateri dan data"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Matikan data mudah alih?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Anda tidak akan mempunyai akses kepada data atau Internet melalui <xliff:g id="CARRIER">%s</xliff:g>. Internet hanya tersedia melaui Wi-Fi."</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Anda tidak akan dapat mengakses data atau Internet melalui <xliff:g id="CARRIER">%s</xliff:g>. Internet hanya akan tersedia melalui Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"pembawa anda"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Tukar kembali kepada <xliff:g id="CARRIER">%s</xliff:g>?"</string>
     <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Data mudah alih tidak akan ditukar secara automatik berdasarkan ketersediaan"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ketik untuk mendapatkan maklumat lanjut"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Tiada penggera"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"masukkan kunci skrin"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Penderia cap jari"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"sahkan"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"akses peranti"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"terlipat"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tidak terlipat"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateri tinggal <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateri stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Sambungkan stilus anda kepada pengecas"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateri stilus lemah"</string>
     <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Digunakan baru-baru ini oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Digunakan oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Digunakan baru-baru ini oleh <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Berbilang tugas"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Pintasan apl"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kebolehaksesan"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index a376ecf..eae8e1d 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -106,7 +106,7 @@
     <string name="screenrecord_title" msgid="4257171601439507792">"ဖန်သားပြင်ရိုက်ကူးစက်"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"စကရင်ရိုက်ကူးမှု အပြီးသတ်နေသည်"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ရိုက်သံဖမ်းခြင်း စတင်မလား။"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ရုပ်သံဖမ်းခြင်း စတင်မလား။"</string>
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ရုပ်သံဖမ်းနေစဉ် Android သည် သင့်ဖန်သားပြင်တွင် မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
     <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"အက်ပ်တစ်ခုကို ရုပ်သံဖမ်းနေစဉ် Android သည် ယင်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
     <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ရုပ်သံ စဖမ်းရန်"</string>
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ကြည့်ရှုရန် တို့ပါ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ဖန်သားပြင်ရိုက်ကူးမှုကို သိမ်းရာတွင် အမှားရှိသည်"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ဖန်သားပြင် ရိုက်ကူးမှု စတင်ရာတွင် အမှားအယွင်းရှိနေသည်"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ပြဿနာရိုက်ကူးစနစ်"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ပြဿနာရိုက်ကူးမှု လုပ်နေသည်"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ပြဿနာစုစည်းခြင်း စက်ရှင်အတွက် လုပ်ဆောင်နေဆဲ အကြောင်းကြားချက်"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ပြဿနာရိုက်ကူးမှုကို သိမ်း၍မရပါ"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ပြဿနာရိုက်ကူးမှုကို စတင်၍မရပါ"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"ဖန်သားပြင်အပြည့် ကြည့်နေသည်"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"ထွက်ရန် အပေါ်မှ အောက်သို့ ပွတ်ဆွဲပါ။"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"နားလည်ပြီ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"နောက်သို့"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"ပင်မစာမျက်နှာ"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"သိမ်းထားသည်"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"စသုံးရန်"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ပြန်ဖွင့်ရန်"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‘အမြန် မျှဝေပါ’ နှင့် Find My Device ကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"မနက်ဖြန်နံနက်တွင် ဘလူးတုသ် ပွင့်ပါမည်"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"အော်ဒီယို မျှဝေခြင်း"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"အော်ဒီယို မျှဝေနေသည်"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"အသံမျှဝေရန်"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"အသံမျှဝေနေသည်"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"စက်အသုံးပြုမှု၏ မည်သည့်အပိုင်းကို သက်ရောက်သလဲ။"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ပြဿနာအမျိုးအစား ရွေးရန်"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ဖန်သားပြင်ရိုက်ကူးရန်"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"စွမ်းဆောင်ရည်"</item>
-    <item msgid="1627504621139124393">"သုံးသူအတွက် ကြားခံစနစ်"</item>
-    <item msgid="8309220355268900335">"ဘက်ထရီ"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"စွမ်းဆောင်ရည်"</string>
+    <string name="user_interface" msgid="3712869377953950887">"သုံးသူအတွက် ကြားခံစနစ်"</string>
+    <string name="thermal" msgid="6758074791325414831">"အပူဓာတ်"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လက်တစ်ဖက်သုံးမုဒ်"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"နားကြားကိရိယာ"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"သုံးနေသည်"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ချိတ်ဆက်မထားပါ"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"နားကြားကိရိယာ"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"စက်အသစ်တွဲချိတ်ရန်"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"စက်အသစ် တွဲချိတ်ရန် နှိပ်ပါ"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ခလုတ် <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"ပင်မ"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"နောက်သို့"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"အပေါ်ညွှန်မြား"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"အောက်ညွှန်မြား"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ဘယ်ညွှန်မြား"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ညာညွှန်မြား"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"ဌာန"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter ခလုတ်"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"နောက်ထပ်အချက်အလက်များအတွက် တို့ပါ"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"နှိုးစက်ပေးမထားပါ"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ဖန်သားပြင်လော့ခ် ထည့်ရန်"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"လက်ဗွေ အာရုံခံကိရိယာ"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"အထောက်အထားစိစစ်ရန်"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"စက်ပစ္စည်းသို့ ဝင်ရန်"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ခေါက်ထားသည်"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ဖြန့်ထားသည်"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"ဘက်ထရီ <xliff:g id="PERCENTAGE">%s</xliff:g> ကျန်သေးသည်"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"စတိုင်လပ်စ် ဘက်ထရီ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"စတိုင်လပ်စ်ကို အားသွင်းကိရိယာနှင့် ချိတ်ဆက်ခြင်း"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"စတိုင်လပ်စ် ဘက်ထရီ အားနည်းနေသည်"</string>
     <string name="video_camera" msgid="7654002575156149298">"ဗီဒီယိုကင်မရာ"</string>
@@ -1327,6 +1352,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string>
     <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 23dc7fd..ebd37f4 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Trykk for å se"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Feil ved lagring av skjermopptaket"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Feil ved start av skjermopptaket"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Funksjon for opptak av problemer"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Opptak, databehandlingsproblem"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Pågående varsel for en innsamlingsøkt for et problem"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Feil ved lagring av problemopptaket"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Feil ved start av problemopptaket"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visning i fullskjerm"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Sveip ned fra toppen for å avslutte."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Greit"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Tilbake"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Startside"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koble fra"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiver"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funksjoner som Quick Share og Finn enheten min bruker Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth slås på i morgen tidlig"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Del lyd"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Deler lyd"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Ytelse"</item>
-    <item msgid="1627504621139124393">"Brukergrensesnitt"</item>
-    <item msgid="8309220355268900335">"Batteri"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Ytelse"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Brukergrensesnitt"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termisk"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frakoblet"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Koble til en ny enhet"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>-knappen"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Startskjerm"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Tilbake"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Oppoverpil"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Nedoverpil"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Venstrepil"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Høyrepil"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midttasten"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Mellomrom"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Trykk for å få mer informasjon"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ingen alarm angitt"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"legg inn skjermlåsen"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeravtrykkssensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentiser"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"åpne enheten"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"lagt sammen"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"åpen"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> batteri gjenstår"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteri i pekepennen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koble pekepennen til en lader"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Det er lite batteri i pekepennen"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nylig brukt av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"I bruk av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nylig brukt av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Inndata"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snarveier"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tilgjengelighet"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index f442427..f83f77c 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"हेर्नका लागि ट्याप गर्नुहोस्"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"स्क्रिन रेकर्डिङ सेभ गर्ने क्रममा त्रुटि भयो"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रिन रेकर्ड गर्न थाल्ने क्रममा त्रुटि भयो"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"समस्यासम्बन्धी जानकारी रेकर्ड गर्ने रेकर्डर"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"समस्याको रेकर्डिङ प्रोसेस गरिँदै छ"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"समस्यासम्बन्धी जानकारी सङ्कलन गर्ने जारी सत्रसम्बन्धी सूचना"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"समस्याको रेकर्डिङ सेभ गर्ने क्रममा त्रुटि भयो"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"समस्यासम्बन्धी जानकारी रेकर्ड गर्न थाल्ने क्रममा त्रुटि भयो"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"फुल स्क्रिन हेरिँदै छ"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"बाहिरिन सिरानबाट तलतिर स्वाइप गर्नुहोस्।"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"बुझेँ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"पछाडि"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"गृह"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेभ गरिएको छ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट गर्नुहोस्"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"एक्टिभेट गर्नुहोस्"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गर्नुहोस्"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक सेयर र Find My Device जस्ता सुविधाहरू प्रयोग गर्न ब्लुटुथ चाहिन्छ"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लुटुथ भोलि बिहान अन हुने छ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"अडियो सेयरिङ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"अडियो सेयर गरिँदै छ"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string>
@@ -361,26 +391,25 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तपाईंको डिभाइसको कुन चाहिँ सुविधा प्रभावित भएको छ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्याको प्रकार चयन गर्नुहोस्"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रिन रेकर्ड"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"पर्फर्मेन्स"</item>
-    <item msgid="1627504621139124393">"युजर इन्टरफेस"</item>
-    <item msgid="8309220355268900335">"ब्याट्री"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"पर्फर्मेन्स"</string>
+    <string name="user_interface" msgid="3712869377953950887">"युजर इन्टरफेस"</string>
+    <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एक हाते मोड"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"हियरिङ डिभाइसहरू"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"सक्रिय छ"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट गरिएको छ"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"हियरिङ डिभाइसहरू"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नयाँ डिभाइसमा कनेक्ट गर्न क्लिक गर्नुहोस्"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रिसेट अपडेट गर्न सकिएन"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"पूर्वनिर्धारित"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"लाइभ क्याप्सन"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string>
-    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
-    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
-    <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string>
+    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string>
+    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string>
+    <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string>
     <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"माइक्रोफोन ब्लक गरिएको छ"</string>
     <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"क्यामेरा ब्लक गरिएको छ"</string>
     <string name="sensor_privacy_start_use_mic_camera_blocked_dialog_title" msgid="195236134743281973">"माइक र क्यामेरा ब्लक गरिएको छ"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> बटन"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"पछाडि"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"अप एरो"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"डाउन एरो"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"लेफ्ट एरो"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"राइट एरो"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"केन्द्र"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"स्पेस"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -933,7 +957,7 @@
     <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"कुनै स्वचालित नियम वा एपले बाधा नपुऱ्याउनुहोस् नामक विकल्पलाई सक्रिय गऱ्यो।"</string>
     <string name="running_foreground_services_title" msgid="5137313173431186685">"पृष्ठभूमिमा चल्ने एपहरू"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string>
-    <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा निष्क्रिय पार्ने हो?"</string>
+    <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा अफ गर्ने हो?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"तपाईं <xliff:g id="CARRIER">%s</xliff:g> मार्फत डेटा वा इन्टरनेट प्रयोग गर्न सक्नुहुने छैन। Wi-Fi मार्फत मात्र इन्टरनेट उपलब्ध हुने छ।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तपाईंको सेवा प्रदायक"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"फेरि <xliff:g id="CARRIER">%s</xliff:g> को मोबाइल डेटा अन गर्ने हो?"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"थप जानकारी प्राप्त गर्न ट्याप गर्नुहोस्"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"अलार्म राखिएको छैन"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"स्क्रिन लक हाल्नुहोस्"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फिंगरप्रिन्ट सेन्सर"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"प्रमाणित गर्नुहोस्"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिभाइस हाल्नुहोस्"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड गरिएको"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"अनफोल्ड गरिएको"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ब्याट्री बाँकी छ"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलसको ब्याट्री <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"आफ्नो स्टाइलस चार्जरमा कनेक्ट गर्नुहोस्"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string>
     <string name="video_camera" msgid="7654002575156149298">"भिडियो क्यामेरा"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ले हालसालै प्रयोग गरेको"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ले प्रयोग गरिरहेको छ"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ले हालसालै प्रयोग गरेको"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"सिस्टम"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"एकै पटक एकभन्दा बढी एप चलाउन मिल्ने सुविधा"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"इनपुट"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"एपका सर्टकटहरू"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"सर्वसुलभता"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"किबोर्डका सर्टकटहरू"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index a68e8a2..110b59e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tik om te bekijken"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Fout bij opslaan van schermopname"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Fout bij starten van schermopname"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Problemen opnemen"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Probleemopname verwerken"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Melding over actieve activiteit voor een sessie voor probleemverzameling"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Fout bij opslaan van probleemopname"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Fout bij starten van probleemopname"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Volledig scherm wordt getoond"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Swipe omlaag vanaf de bovenkant van het scherm om af te sluiten."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Terug"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Startscherm"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"loskoppelen"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activeren"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Functies zoals Quick Share en Vind mijn apparaat gebruiken bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wordt morgenochtend aangezet"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio delen"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio delen…"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Op welk onderdeel van de apparaatfunctionaliteit had dit effect?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Probleemtype selecteren"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Schermopname"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Prestaties"</item>
-    <item msgid="1627504621139124393">"Gebruikersinterface"</item>
-    <item msgid="8309220355268900335">"Batterij"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Prestaties"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Gebruikersinterface"</string>
+    <string name="thermal" msgid="6758074791325414831">"Thermisch"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hoortoestellen"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actief"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppeld"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hoortoestellen"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Nieuw apparaat koppelen"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knop <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Terug"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pijl-omhoog"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pijl-omlaag"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Pijl-links"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Pijl-rechts"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Midden"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Spatiebalk"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tik hier voor meer informatie"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Geen wekker gezet"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"schermvergrendeling invoeren"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Vingerafdruksensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"verifiëren"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"apparaat opgeven"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dichtgevouwen"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opengevouwen"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Nog <xliff:g id="PERCENTAGE">%s</xliff:g> batterijlading"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stylusbatterij <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Verbind je stylus met een oplader"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Batterij van stylus bijna leeg"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video­camera"</string>
@@ -1318,26 +1345,26 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Recent gebruikt door <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Gebruikt door <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Recent gebruikt door <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Systeem"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Invoer"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-snelkoppelingen"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toegankelijkheid"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string>
-    <string name="home_controls_dream_label" msgid="6567105701292324257">"Huisbediening"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Snel toegang tot je huisbediening als screensaver"</string>
+    <string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Gebruik bediening voor in huis als screensaver"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 4bdd980..2c60c2b 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ଦେଖିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ସ୍କ୍ରିନ ରେକର୍ଡିଂ ସେଭ କରିବାରେ ତ୍ରୁଟି"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ସ୍କ୍ରିନ୍ ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବାରେ ତ୍ରୁଟି"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ସମସ୍ୟା ରେକର୍ଡର"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ସମସ୍ୟାର ରେକର୍ଡିଂର ପ୍ରକ୍ରିୟାକରଣ"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ଏକ ସମସ୍ୟା ସଂଗ୍ରହ ସେସନ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ସମସ୍ୟାର ରେକର୍ଡିଂ କରିବାରେ ତ୍ରୁଟି"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ସମସ୍ୟାର ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବାରେ ତ୍ରୁଟି"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନରେ ଦେଖିବା"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"ବାହାରି ଯିବା ପାଇଁ, ଶୀର୍ଷରୁ ତଳକୁ ସ୍ୱାଇପ କରନ୍ତୁ।"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"ବୁଝିଗଲି"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"ଫେରନ୍ତୁ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"ହୋମ"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ସେଭ କରାଯାଇଛି"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ଚାଲୁ କରନ୍ତୁ"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ଏବଂ Find My Device ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ବ୍ଲୁଟୁଥ ଆସନ୍ତା କାଲି ସକାଳେ ଚାଲୁ ହେବ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ଅଡିଓ ସେୟାରିଂ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍‍"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ଆପଣଙ୍କ ଡିଭାଇସ ଅନୁଭୂତିର କେଉଁ ଅଂଶ ପ୍ରଭାବିତ ହୋଇଛି?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ସମସ୍ୟାର ପ୍ରକାର ଚୟନ କରନ୍ତୁ"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ସ୍କ୍ରିନ ରେକର୍ଡ"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"ପରଫରମାନ୍ସ"</item>
-    <item msgid="1627504621139124393">"ୟୁଜର ଇଣ୍ଟରଫେସ"</item>
-    <item msgid="8309220355268900335">"ବେଟେରୀ"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"ପରଫରମାନ୍ସ"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ୟୁଜର ଇଣ୍ଟରଫେସ"</string>
+    <string name="thermal" msgid="6758074791325414831">"ଥର୍ମାଲ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ଏକ-ହାତ ମୋଡ"</string>
-    <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string>
-    <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ହିଅରିଂ ଡିଭାଇସଗୁଡ଼ିକ"</string>
+    <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ସକ୍ରିୟ"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ଡିସକନେକ୍ଟ ହୋଇଛି"</string>
+    <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ନୂଆ ଡିଭାଇସ ପେୟାର କରନ୍ତୁ"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ପ୍ରିସେଟକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ପ୍ରିସେଟ"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"ଲାଇଭ କେପ୍ସନ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ କରିବେ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ଡିଭାଇସର କେମେରାକୁ ଅନବ୍ଲକ କରିବେ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ଡିଭାଇସର କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ବଟନ୍‍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"ହୋମ"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"ଫେରନ୍ତୁ"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ଅପ ତୀର କୀ"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ଡାଉନ ଆରୋ"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ବାମ ତୀର କୀ"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ଡାହାଣ ତୀର କୀ"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"କେନ୍ଦ୍ର"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"ସ୍ପେସ୍‍"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"ଏଣ୍ଟର୍"</string>
@@ -934,7 +958,7 @@
     <string name="running_foreground_services_title" msgid="5137313173431186685">"ବ୍ୟାକଗ୍ରାଉଣ୍ଡରେ ଆପ୍‍ ଚାଲୁଛି"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"ମୋବାଇଲ୍‌ ଡାଟା ବନ୍ଦ କରିବେ?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟରନେଟ୍‌କୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର  ଆକ୍ସେସ୍ ରହିବ ନାହିଁ। ଇଣ୍ଟରନେଟ୍‌ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟର୍ନେଟକୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ ରହିବ ନାହିଁ। ଇଣ୍ଟର୍ନେଟ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ଆପଣଙ୍କ କେରିଅର୍"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g>କୁ ପୁଣି ସ୍ୱିଚ କରିବେ?"</string>
     <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ଉପଲବ୍ଧତା ଆଧାରରେ ମୋବାଇଲ ଡାଟା ସ୍ୱତଃ ସ୍ୱିଚ ହେବ ନାହିଁ"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ଅଧିକ ସୂଚନା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ଆଲାରାମ ସେଟ ହୋଇନାହିଁ"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ସ୍କ୍ରିନ ଲକ ଲେଖନ୍ତୁ"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ଟିପଚିହ୍ନ ସେନ୍ସର୍"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ପ୍ରମାଣୀକରଣ କରନ୍ତୁ"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ଡିଭାଇସ୍ ବିଷୟରେ ସୂଚନା ଲେଖନ୍ତୁ"</string>
@@ -1211,7 +1237,7 @@
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}other{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"ନୂଆ ସୂଚନା"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ସକ୍ରିୟ ଆପ୍ସ"</string>
-    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବ୍ୟାଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string>
+    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବେଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ବନ୍ଦ ହୋଇଛି"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"ହୋଇଗଲା"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ଫୋଲ୍ଡେଡ"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ଅନଫୋଲ୍ଡେଡ"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ବେଟେରୀ ଚାର୍ଜ ବାକି ଅଛି"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ଷ୍ଟାଇଲସ ବେଟେରୀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ଏକ ଚାର୍ଜର ସହ ଆପଣଙ୍କ ଷ୍ଟାଇଲସକୁ କନେକ୍ଟ କରନ୍ତୁ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ଷ୍ଟାଇଲସ ବେଟେରୀର ଚାର୍ଜ କମ ଅଛି"</string>
     <string name="video_camera" msgid="7654002575156149298">"ଭିଡିଓ କେମେରା"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ଏବେ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ଏବେ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"ସିଷ୍ଟମ"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ମଲ୍ଟିଟାସ୍କିଂ"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ଇନପୁଟ"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ଆପ ସର୍ଟକଟ"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ଆକ୍ସେସିବିଲିଟୀ"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 2558fca..54a763d 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋ ਗਈ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋਈ"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਰ"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਿੰਗ ਪ੍ਰਕਿਰਿਆ-ਅਧੀਨ ਹੈ"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ਸਮੱਸਿਆ ਬਾਰੇ ਜਾਣਕਾਰੀ ਇਕੱਤਰ ਕਰਨ ਵਾਲੇ ਸੈਸ਼ਨ ਸੰਬੰਧੀ ਨਿਰੰਤਰ ਸੂਚਨਾ"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਿੰਗ ਰੱਖਿਅਤ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋ ਗਈ"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ਸਮੱਸਿਆ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋ ਗਈ"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦੇਖਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"ਬਾਹਰ ਜਾਣ ਲਈ, ਉਪਰੋਂ ਹੇਠਾਂ ਵੱਲ ਸਵਾਈਪ ਕਰੋ।"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"ਸਮਝ ਲਿਆ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"ਪਿੱਛੇ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"ਘਰ"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ਕਵਿੱਕ ਸ਼ੇਅਰ ਅਤੇ Find My Device ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਵਰਤਦੀਆਂ ਹਨ"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਕਿਹੜੀ ਸੁਵਿਧਾ ਪ੍ਰਭਾਵਿਤ ਹੋਈ ਸੀ?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ਸਮੱਸਿਆ ਦੀ ਕਿਸਮ ਚੁਣੋ"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"ਕਾਰਗੁਜ਼ਾਰੀ"</item>
-    <item msgid="1627504621139124393">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</item>
-    <item msgid="8309220355268900335">"ਬੈਟਰੀ"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"ਕਾਰਗੁਜ਼ਾਰੀ"</string>
+    <string name="user_interface" msgid="3712869377953950887">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</string>
+    <string name="thermal" msgid="6758074791325414831">"ਥਰਮਲ"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ਇੱਕ ਹੱਥ ਮੋਡ"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ਕਿਰਿਆਸ਼ੀਲ"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ਡਿਸਕਨੈਕਟ ਹੈ"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"\'ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ\' \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ਬਟਨ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ਉੱਪਰ ਤੀਰ"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ਹੇਠਾਂ ਤੀਰ"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ਖੱਬਾ ਤੀਰ"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ਸੱਜਾ ਤੀਰ"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ਕੋਈ ਅਲਾਰਮ ਸੈੱਟ ਨਹੀਂ"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ਸਕ੍ਰੀਨ ਲਾਕ ਦਾਖਲ ਕਰੋ"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ਪ੍ਰਮਾਣਿਤ ਕਰੋ"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ਡੀਵਾਈਸ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ਅਣਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ ਬਾਕੀ"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ਆਪਣੇ ਸਟਾਈਲਸ ਨੂੰ ਚਾਰਜਰ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ ਘੱਟ ਹੈ"</string>
     <string name="video_camera" msgid="7654002575156149298">"ਵੀਡੀਓ ਕੈਮਰਾ"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ਹਾਲ ਹੀ ਵਿੱਚ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ਵੱਲੋਂ ਵਰਤਿਆ ਗਿਆ"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ਵੱਲੋਂ ਵਰਤੋਂ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ਹਾਲ ਹੀ ਵਿੱਚ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ਵੱਲੋਂ ਵਰਤਿਆ ਗਿਆ"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"ਸਿਸਟਮ"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ਮਲਟੀਟਾਸਕਿੰਗ"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ਇਨਪੁੱਟ"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ਐਪ ਸ਼ਾਰਟਕੱਟ"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ਪਹੁੰਚਯੋਗਤਾ"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 747d922..39e243d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Kliknij, aby wyświetlić"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Podczas zapisywania nagrania ekranu wystąpił błąd"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Błąd podczas rozpoczynania rejestracji zawartości ekranu"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Rejestrator problemów"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Przetwarzam nagranie problemu"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Powiadomienie o trwającej aktywności sesji nagrywania ekranu"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Błąd podczas zapisywania nagrania problemu"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Błąd podczas rozpoczynania nagrania problemu"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Włączony pełny ekran"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Aby wyjść, przesuń palcem z góry na dół."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Wróć"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Ekran główny"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"rozłącz"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktywuj"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetootha używają funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Udostępnianie dźwięku"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Udostępniam dźwięk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Udostępnij dźwięk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Udostępnia dźwięk"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Którego aspektu korzystania z urządzenia dotyczył problem?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Wybierz typ problemu"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Nagrywanie ekranu"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Wydajność"</item>
-    <item msgid="1627504621139124393">"Interfejs"</item>
-    <item msgid="8309220355268900335">"Bateria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Wydajność"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interfejs"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termografia"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Urządzenia słuchowe"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktywny"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Rozłączono"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Urządzenia słuchowe"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sparuj nowe urządzenie"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Przycisk <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Wstecz"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Strzałka w górę"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Strzałka w dół"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Strzałka w lewo"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Strzałka w prawo"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Do środka"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Spacja"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Kliknij, aby uzyskać więcej informacji"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nie ustawiono alarmu"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"Wprowadź blokadę ekranu"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Czytnik linii papilarnych"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"uwierzytelnij"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"otwórz urządzenie"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"po zamknięciu"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"po otwarciu"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Pozostało <xliff:g id="PERCENTAGE">%s</xliff:g> baterii"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria rysika: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Podłącz rysik do ładowarki"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Słaba bateria w rysiku"</string>
     <string name="video_camera" msgid="7654002575156149298">"Kamera"</string>
@@ -1318,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Ostatnio używany przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Używany przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ostatnio używany przez aplikację <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Wielozadaniowość"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Wprowadzanie"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Skróty do aplikacji"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ułatwienia dostępu"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index a3bfad7..dc7d564 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Toque para ver"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Erro ao salvar a gravação da tela"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Erro ao iniciar a gravação de tela"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Gravador de problemas"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processando a gravação do problema"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificação em andamento para uma sessão de coleta de problemas"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Erro ao salvar a gravação do problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Erro ao iniciar a gravação do problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visualização em tela cheia"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Para sair, deslize de cima para baixo."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Entendi"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Voltar"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Página inicial"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Desempenho"</item>
-    <item msgid="1627504621139124393">"Interface do usuário"</item>
-    <item msgid="8309220355268900335">"Bateria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string>
+    <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
@@ -594,7 +624,7 @@
     <string name="screen_pinning_negative" msgid="6882816864569211666">"Agora não"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"App fixado"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"App liberado"</string>
-    <string name="stream_voice_call" msgid="7468348170702375660">"Ligar"</string>
+    <string name="stream_voice_call" msgid="7468348170702375660">"Ligações"</string>
     <string name="stream_system" msgid="7663148785370565134">"Sistema"</string>
     <string name="stream_ring" msgid="7550670036738697526">"Toques"</string>
     <string name="stream_music" msgid="2188224742361847580">"Mídia"</string>
@@ -634,7 +664,7 @@
     <string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string>
     <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string>
     <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
-    <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string>
+    <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Seta para cima"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Seta para baixo"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Seta para a esquerda"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Seta para a direita"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centralizar"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para mais informações"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme definido"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"inserir bloqueio de tela"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressão digital"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
@@ -1210,7 +1237,7 @@
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string>
-    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string>
+    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateria restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
     <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em uso pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index d76aa8de..0b74941 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Toque para ver"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Erro ao guardar a gravação de ecrã"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ocorreu um erro ao iniciar a gravação do ecrã."</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Registador de problemas"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"A proc. registo de problemas"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificação em curso de uma sessão de recolha de problemas"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Erro ao guardar o registo de problemas"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Erro ao iniciar o registo de problemas"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visualização de ecrã inteiro"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Para sair, deslize rapidamente para baixo a partir da parte superior."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Anterior"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Página inicial"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcionalidades como a Partilha rápida e o serviço Localizar o meu dispositivo usam o Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth vai ser ativado amanhã de manhã"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partilha de áudio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"A partilhar áudio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da experiência do disposit. foi afetada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecione o tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de ecrã"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Desempenho"</item>
-    <item msgid="1627504621139124393">"Interface do utilizador"</item>
-    <item msgid="8309220355268900335">"Bateria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interface do utilizador"</string>
+    <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desligados"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sincronizar novo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Início"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Anterior"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Seta para cima"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Seta para baixo"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Seta para a esquerda"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Seta para a direita"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Ao centro"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Espaço"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -933,7 +958,7 @@
     <string name="running_foreground_services_title" msgid="5137313173431186685">"Apps em execução em segundo plano"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não vai ter acesso aos dados nem à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet vai estar disponível só por Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"o seu operador"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Mudar de novo para <xliff:g id="CARRIER">%s</xliff:g>?"</string>
     <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Os dados móveis não vão mudar automaticamente com base na disponibilidade"</string>
@@ -988,7 +1013,7 @@
     <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fechar definições de ampliação"</string>
     <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Sair do modo de edição"</string>
     <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastar o canto para redimensionar"</string>
-    <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir deslocamento da página na diagonal"</string>
+    <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir deslocamento diagonal"</string>
     <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionar"</string>
     <string name="accessibility_change_magnification_type" msgid="666000085077432421">"Alterar tipo de ampliação"</string>
     <string name="accessibility_magnification_end_resizing" msgid="4881690585800302628">"Terminar redimensionamento"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para obter mais informações"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme definido"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"introduzir bloqueio de ecrã"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressões digitais"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"entrar no dispositivo"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> de bateria restante"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da caneta stylus a <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ligue a caneta stylus a um carregador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da caneta stylus fraca"</string>
     <string name="video_camera" msgid="7654002575156149298">"Câmara de vídeo"</string>
@@ -1327,6 +1354,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
     <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>
+    <!-- no translation found for touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index a3bfad7..dc7d564 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Toque para ver"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Erro ao salvar a gravação da tela"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Erro ao iniciar a gravação de tela"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Gravador de problemas"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Processando a gravação do problema"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificação em andamento para uma sessão de coleta de problemas"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Erro ao salvar a gravação do problema"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Erro ao iniciar a gravação do problema"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visualização em tela cheia"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Para sair, deslize de cima para baixo."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Entendi"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Voltar"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Página inicial"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Desempenho"</item>
-    <item msgid="1627504621139124393">"Interface do usuário"</item>
-    <item msgid="8309220355268900335">"Bateria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string>
+    <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
@@ -594,7 +624,7 @@
     <string name="screen_pinning_negative" msgid="6882816864569211666">"Agora não"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"App fixado"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"App liberado"</string>
-    <string name="stream_voice_call" msgid="7468348170702375660">"Ligar"</string>
+    <string name="stream_voice_call" msgid="7468348170702375660">"Ligações"</string>
     <string name="stream_system" msgid="7663148785370565134">"Sistema"</string>
     <string name="stream_ring" msgid="7550670036738697526">"Toques"</string>
     <string name="stream_music" msgid="2188224742361847580">"Mídia"</string>
@@ -634,7 +664,7 @@
     <string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string>
     <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string>
     <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string>
-    <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string>
+    <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string>
     <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string>
     <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string>
     <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Botão <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Seta para cima"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Seta para baixo"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Seta para a esquerda"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Seta para a direita"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centralizar"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para mais informações"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme definido"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"inserir bloqueio de tela"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressão digital"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
@@ -1210,7 +1237,7 @@
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string>
-    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string>
+    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Bateria restante: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string>
     <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em uso pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Usado recentemente pelo app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistema"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitarefas"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Entrada"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Atalhos de apps"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index c24ab85..f735be4 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Atinge pentru a afișa"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Eroare la salvarea înregistrării ecranului"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Eroare la începerea înregistrării ecranului"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Instrument de înregistrare a problemelor"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Se procesează înregistrarea problemei"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notificare în curs pentru o sesiune de înregistrare a problemei"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Eroare la salvarea înregistrării problemei"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Eroare la începerea înregistrării problemei"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Vizualizare pe ecran complet"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Pentru a ieși, glisează de sus în jos."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Înapoi"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Ecranul de pornire"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deconectează"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activează"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcții precum Quick Share și Găsește-mi dispozitivul folosesc Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se va activa mâine dimineață"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Permiterea accesului la audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Se permite accesul la conținutul audio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performanță"</item>
-    <item msgid="1627504621139124393">"Interfața de utilizare"</item>
-    <item msgid="8309220355268900335">"Baterie"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performanță"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Interfața de utilizare"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparate auditive"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activ"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deconectat"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparate auditive"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Asociază un nou dispozitiv"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nu s-a putut actualiza presetarea"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Presetare"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Subtitrări live"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblochezi microfonul dispozitivului?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblochezi camera dispozitivului?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblochezi camera și microfonul dispozitivului?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Butonul <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"La început"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Înapoi"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Săgeată în sus"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Săgeată în jos"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Săgeată spre stânga"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Săgeată spre dreapta"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"În centru"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Spațiu"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Atinge pentru mai multe informații"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nicio alarmă setată"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"intră în blocarea ecranului"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor de amprentă"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifică-te"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesează dispozitivul"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"închis"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"deschis"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> baterie rămasă"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria creionului: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conectează-ți creionul la un încărcător"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Nivelul bateriei creionului este scăzut"</string>
     <string name="video_camera" msgid="7654002575156149298">"Cameră video"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Folosit recent de <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Se folosește pentru <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Folosit recent de <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Intrare"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Comenzi rapide pentru aplicații"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilitate"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 2313a93..d7973fd 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Нажмите, чтобы посмотреть."</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Не удалось сохранить запись видео с экрана."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Не удалось начать запись видео с экрана."</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Запись проблем на видео"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Обрабатываем запись"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Текущее уведомление о записи проблемы на видео"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Не удалось сохранить запись."</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Не удалось начать запись."</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Полноэкранный режим"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Чтобы выйти, проведите по экрану сверху вниз."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"ОК"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Назад"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Главный экран"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сохранено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"отключить"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активировать"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth используется в таких функциях и сервисах, как \"Быстрая отправка\" и \"Найти устройство\""</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth включится завтра утром"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Отправка аудио"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Включена отправка аудио"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string>
@@ -361,25 +391,24 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С чем связана проблема, с которой вы столкнулись?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберите тип проблемы"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запись экрана"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Производительность"</item>
-    <item msgid="1627504621139124393">"Интерфейс"</item>
-    <item msgid="8309220355268900335">"Батарея"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Производительность"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Интерфейс"</string>
+    <string name="thermal" msgid="6758074791325414831">"Тепловизор"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слуховые аппараты"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не подключено"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слуховые аппараты"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Подключить новое устройство"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не удалось обновить набор настроек."</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор настроек"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Автоматические субтитры"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблокировать камеру и микрофон устройства?"</string>
-    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование микрофона."</string>
-    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование камеры."</string>
+    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"Все приложения и сервисы, у которых есть разрешение на использование микрофона, получат к нему доступ."</string>
+    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"Все приложения и сервисы, у которых есть разрешение на использование камеры, получат к ней доступ."</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"Будет снята блокировка доступа для всех приложений и сервисов с разрешением на использование камеры или микрофона."</string>
     <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"Микрофон заблокирован"</string>
     <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"Камера заблокирована"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Главный экран"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелка вверх"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелка вниз"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелка влево"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелка вправо"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центральная стрелка"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Пробел"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Ввод"</string>
@@ -933,8 +957,8 @@
     <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Режим \"Не беспокоить\" был включен специальным правилом или приложением."</string>
     <string name="running_foreground_services_title" msgid="5137313173431186685">"Приложения, работающие в фоновом режиме"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"Нажмите, чтобы проверить энергопотребление и трафик"</string>
-    <string name="mobile_data_disable_title" msgid="5366476131671617790">"Отключить мобильный Интернет?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Вы не сможете передавать данные или выходить в Интернет через оператора \"<xliff:g id="CARRIER">%s</xliff:g>\". Интернет будет доступен только по сети Wi-Fi."</string>
+    <string name="mobile_data_disable_title" msgid="5366476131671617790">"Отключить мобильный интернет?"</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Вы не сможете передавать данные или выходить в интернет через оператора \"<xliff:g id="CARRIER">%s</xliff:g>\". Интернет будет доступен только по сети Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ваш оператор"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Переключиться на сеть \"<xliff:g id="CARRIER">%s</xliff:g>\"?"</string>
     <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Мобильный интернет не будет переключаться автоматически."</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Нажмите, чтобы узнать больше."</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Нет будильников"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"разблокировать экран"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер отпечатков пальцев"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"выполнить аутентификацию"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"указать устройство"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"устройство сложено"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"устройство разложено"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Уровень заряда батареи: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батарея стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поставьте стилус на зарядку."</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Низкий заряд батареи стилуса"</string>
     <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Недавно использовалось приложением \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Сейчас используется приложением \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Недавно использовалось приложением \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Система"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Многозадачность"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ввод"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ярлыки приложений"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Специальные возможности"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Быстрые клавиши"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 91a6843..dac0c42 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"බැලීමට තට්ටු කරන්න"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"තිර පටිගත කිරීම සුරැකීමේ දෝෂයකි"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"තිර පටිගත කිරීම ආරම්භ කිරීමේ දෝෂයකි"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ගැටලු රෙකෝඩරය"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ගැටලු වාර්තාව සැකසුම් කිරීම"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ගැටලු එකතු කිරීමේ සැසියක් සඳහා දැනට පවතින දැනුම්දීම"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ගැටලුව සටහන් කිරීම සුරැකීමේ දෝෂය"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ගැටලුව වාර්තා කිරීම ආරම්භ කිරීමේ දෝෂය"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"මුළු තිරය බලමින්"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"ඉවත් වීමට, ඉහළ සිට පහළට ස්වයිප් කරන්න"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"තේරුණා"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"ආපසු"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"මුල් පිටුව"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"සුරැකිණි"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"විසන්ධි කරන්න"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"සක්‍රිය කරන්න"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්‍රීයව නැවත ක්‍රියාත්මක කරන්න"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ඉක්මන් බෙදා ගැනීම සහ මගේ උපාංගය සෙවීම වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"බ්ලූටූත් හෙට උදේ සක්‍රීය වෙයි"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ශ්‍රව්‍ය බෙදා ගැනීම"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ශ්‍රව්‍යය බෙදා ගැනීම"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්‍රව්‍ය"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ඔබේ උපාංග අත්දැකීමේ කුමන කොටසට බලපෑවේ ද?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ගැටලු වර්ගය තෝරන්න"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"තිර පටිගත කිරීම"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"කාර්ය සාධනය"</item>
-    <item msgid="1627504621139124393">"පරිශීලක අතුරු මුහුණත"</item>
-    <item msgid="8309220355268900335">"බැටරිය"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"කාර්ය සාධනය"</string>
+    <string name="user_interface" msgid="3712869377953950887">"පරිශීලක අතුරු මුහුණත"</string>
+    <string name="thermal" msgid="6758074791325414831">"තාප"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් ප්‍රකාරය"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ශ්‍රවණ උපාංග"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ක්‍රියාකාරී"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"විසන්‍ධි විය"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ශ්‍රවණ උපාංග"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"නව උපාංගය යුගල කරන්න"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"නව උපාංගය යුගල කිරීමට ක්ලික් කරන්න"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තල"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> බොත්තම"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home යතුර"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"ආපසු"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ඉහළ ඊතලය"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"පහළ ඊතලය"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"වම් ඊතලය"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"දකුණු ඊතලය"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"මැද"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"ඉඩ යතුර"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter යතුර"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"තවත් තොරතුරු සඳහා තට්ටු කරන්න"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"එලාම සකසා නැත"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"තිර අගුල ඇතුළු කරන්න"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ඇඟිලි සලකුණු සංවේදකය"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"සත්‍යාපනය කරන්න"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"උපාංගය ඇතුළු කරන්න"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"නැවූ"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"නොනැවූ"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> බැටරිය ඉතිරිව ඇත"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"පන්හිඳ බැටරිය <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ඔබේ පන්හිඳ චාජරයකට සම්බන්ධ කරන්න"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"පන්හිඳ බැටරිය අඩුයි"</string>
     <string name="video_camera" msgid="7654002575156149298">"වීඩියෝ කැමරාව"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් මෑතකදී භාවිත කරන ලදි (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> භාවිත කරයි (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> විසින් මෑතකදී භාවිත කරන ලදි (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"පද්ධතිය"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"බහුකාර්ය"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ආදානය"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"යෙදුම් කෙටිමං"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ප්‍රවේශ්‍යතාව"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"යතුරු පුවරු කෙටි මං"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0df58c2..0014117 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Zobrazte klepnutím"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Pri ukladaní nahrávky obrazovky sa vyskytla chyba"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Pri spustení nahrávania obrazovky sa vyskytla chyba"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Nástroj na zaznamenávanie problémov"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Záznam problému sa spracúva"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Upozornenie na prebiehajúcu aktivitu týkajúcu sa relácie zhromažďovania problémov"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Pri ukladaní záznamu problému sa vyskytla chyba"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Pri spúšťaní záznamu problému sa vyskytla chyba"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Zobrazenie na celú obrazovku"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Ukončíte potiahnutím zhora nadol."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Dobre"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Späť"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Plocha"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojiť"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovať"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcie ako Quick Share a Nájdi moje zariadenie používajú Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sa zapne zajtra ráno"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zdieľanie zvuku"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zdieľa sa zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Zdieľať zvuk"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Zdieľa sa zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte typ problému"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Výkon"</item>
-    <item msgid="1627504621139124393">"Používateľské rozhranie"</item>
-    <item msgid="8309220355268900335">"Batéria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Výkon"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Používateľské rozhranie"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termálne"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednej ruky"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Načúvacie zariadenia"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktívne"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojené"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Načúvacie zariadenia"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovať nové zariadenie"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predvoľba"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Živý prepis"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Chcete odblokovať mikrofón zariadenia?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Chcete odblokovať kameru zariadenia?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Chcete odblokovať fotoaparát a mikrofón zariadenia?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Tlačidlo <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Domov"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Späť"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Šípka nahor"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Šípka nadol"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Šípka doľava"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Šípka doprava"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Do stredu"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Medzerník"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Klepnutím si zobrazíte ďalšie informácie"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Žiadny budík"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"zadať zámku obrazovky"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor odtlačkov prstov"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"overte"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"vstúpte do zariadenia"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zložené"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Zostáva <xliff:g id="PERCENTAGE">%s</xliff:g> batérie"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batéria dotykového pera: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pripojte dotykové pero k nabíjačke"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stav batérie dotykového pera je nízky"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1319,26 +1343,26 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Nedávno využila aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Využíva <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedávno využila aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Systém"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Vstup"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Odkazy do aplikácií"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostupnosť"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string>
-    <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchlejšie ovládanie domácnosti v šetriči obrazov."</string>
+    <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchly prístup k ovládaniu domácnosti z šetriča obrazovky"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 9bbe70e..4428c0c 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Dotaknite se za ogled."</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Napaka pri shranjevanju posnetka zaslona"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Napaka pri začenjanju snemanja zaslona"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Snemalnik težav"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Obdelovanje posnetka težave"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Obvestilo o aktivni dejavnosti za sejo zbiranja težav"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Napaka pri shranjevanju posnetka težave"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Napaka pri zagonu snemanja težave"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Vklopljen je celozaslonski način."</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Zaprete ga tako, da z vrha s prstom povlečete navzdol."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Razumem"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Nazaj"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Začetni zaslon"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinitev povezave"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije, kot sta Hitro deljenje in Poišči mojo napravo, uporabljajo Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se bo vklopil jutri zjutraj"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvoka"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Poteka deljenje zvoka"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na kateri del izkušnje z napravo je to vplivalo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izberite vrsto težave"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Učinkovitost delovanja"</item>
-    <item msgid="1627504621139124393">"Uporabniški vmesnik"</item>
-    <item msgid="8309220355268900335">"Baterija"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Učinkovitost delovanja"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Uporabniški vmesnik"</string>
+    <string name="thermal" msgid="6758074791325414831">"Toplotno"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni pripomočki"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Brez povezave"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni pripomočki"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Seznanitev nove naprave"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string>
@@ -598,7 +628,7 @@
     <string name="stream_system" msgid="7663148785370565134">"Sistem"</string>
     <string name="stream_ring" msgid="7550670036738697526">"Sprožitev zvonjenja"</string>
     <string name="stream_music" msgid="2188224742361847580">"Predstavnost"</string>
-    <string name="stream_alarm" msgid="16058075093011694">"Opozorilo"</string>
+    <string name="stream_alarm" msgid="16058075093011694">"Alarm"</string>
     <string name="stream_notification" msgid="7930294049046243939">"Obvestilo"</string>
     <string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="7322536356554673067">"Dvojna večtonska frekvenca"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Gumb <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Začetek"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Nazaj"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Puščica gor"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Puščica dol"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Puščica levo"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Puščica desno"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Sredina"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Preslednica"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Vnesi"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dotaknite se za več informacij"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ni nastavljenih alarmov"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"odklenite zaslon"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Tipalo prstnih odtisov"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"preverjanje pristnosti"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"vstop v napravo"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zaprto"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"razprto"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Preostanek energije baterije: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Napolnjenost baterije pisala: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisalo s polnilnikom."</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Skoraj prazna baterija pisala"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1327,6 +1354,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</string>
     <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>
+    <!-- no translation found for touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 4aebab4..537134d 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Trokit për të parë"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Gabim gjatë ruajtjes së regjistrimit të ekranit"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Gabim gjatë nisjes së regjistrimit të ekranit"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Regjistruesi i problemeve"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Regjistrimi i problemeve me përpunimin"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Njoftim në vazhdim për një seancë për mbledhjen e problemeve"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Gabim gjatë ruajtjes së regjistrimit të problemit"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Gabim gjatë nisjes së regjistrimit të problemit"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Po shikon ekranin e plotë"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Për të dalë, rrëshqit shpejt poshtë."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"E kuptova"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Prapa"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Faqja bazë"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht sërish nesër"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Veçoritë e tilla si \"Ndarja e shpejtë\" dhe \"Gjej pajisjen time\" përdorin Bluetooth-in"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-i do të aktivizohet nesër në mëngjes"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ndarja e audios"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioja po ndahet"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Ndaj audion"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Audioja po ndahet"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string>
@@ -361,21 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performanca"</item>
-    <item msgid="1627504621139124393">"Ndërfaqja e përdoruesit"</item>
-    <item msgid="8309220355268900335">"Bateria"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performanca"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Ndërfaqja e përdoruesit"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termike"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Pajisje ndihmëse për dëgjimin"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktive"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Shkëputur"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Pajisjet e dëgjimit"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Çifto pajisje të re"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliko për të çiftuar një pajisje të re"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Paravendosja nuk mund të përditësohej"</string>
-    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
-    <skip />
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Paravendosja"</string>
+    <string name="live_caption_title" msgid="8916875614623730005">"Titra në çast"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Të zhbllokohet mikrofoni i pajisjes?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Të zhbllokohet kamera e pajisjes?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Të zhbllokohen kamera dhe mikrofoni i pajisjes?"</string>
@@ -596,9 +622,9 @@
     <string name="screen_pinning_negative" msgid="6882816864569211666">"Jo, faleminderit!"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"Aplikacioni u gozhdua"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"Aplikacioni u zhgozhdua"</string>
-    <string name="stream_voice_call" msgid="7468348170702375660">"Telefono"</string>
+    <string name="stream_voice_call" msgid="7468348170702375660">"Telefonata"</string>
     <string name="stream_system" msgid="7663148785370565134">"Sistemi"</string>
-    <string name="stream_ring" msgid="7550670036738697526">"Bjeri ziles"</string>
+    <string name="stream_ring" msgid="7550670036738697526">"Zilja"</string>
     <string name="stream_music" msgid="2188224742361847580">"Media"</string>
     <string name="stream_alarm" msgid="16058075093011694">"Alarmi"</string>
     <string name="stream_notification" msgid="7930294049046243939">"Njoftimi"</string>
@@ -729,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Butoni <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Kreu"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Prapa"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Shigjeta lart"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Shigjeta poshtë"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Shigjeta majtas"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Shigjeta djathtas"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Qendror"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Hapësirë"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1180,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Trokit për më shumë informacione"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nuk është caktuar asnjë alarm"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"hyr te kyçja e ekranit"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensori i gjurmës së gishtit"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"për ta vërtetuar"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"për të hyrë në pajisje"</string>
@@ -1277,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"palosur"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"shpalosur"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Përqindja e mbetur e baterisë: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria e stilolapsit: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Lidhe stilolapsin me një karikues"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria e stilolapsit në nivel të ulët"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1320,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Përdorur së fundi nga <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Në përdorim nga <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Përdorur së fundi nga <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistemi"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Kryerja e shumë detyrave"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Hyrja"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Shkurtoret e aplikacionit"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qasshmëria"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 0f0d78d..1969746 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Додирните да бисте прегледали"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Грешка при чувању снимка екрана"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Грешка при покретању снимања екрана"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Снимач проблема"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Обрађује се снимак проблема"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"обавештење о активности у току за сесију прикупљања података о проблему"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Грешка при чувању снимка проблема"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Грешка при покретању снимања проблема"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Приказује се цео екран"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Да бисте изашли, превуците надоле одозго."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Важи"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Назад"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Почетна"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сачувано"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекините везу"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирајте"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аутоматски поново укључи сутра"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функције као што су Quick Share и Пронађи мој уређај користе Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ће се укључити сутра ујутру"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Дељење звука"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Дели се звук"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Дели звук"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Дели се звук"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string>
@@ -361,13 +389,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На који део доживљаја на уређају је ово утицало?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изаберите тип проблема"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екрана"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Учинак"</item>
-    <item msgid="1627504621139124393">"Кориснички интерфејс"</item>
-    <item msgid="8309220355268900335">"Батерија"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Перформансе"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string>
+    <string name="thermal" msgid="6758074791325414831">"Термална камера"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим једном руком"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Веза је прекинута"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Упари нови уређај"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликните да бисте упарили нов уређај"</string>
@@ -727,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Дугме <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Тастер Почетна"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Тастер Назад"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрелица нагоре"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрелица надоле"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрелица налево"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрелица надесно"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Тастер са централном стрелицом"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Размак"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Додирните за више информација"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Није подешен"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"унесите откључавање екрана"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отисак прста"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"потврдите идентитет"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"унесите уређај"</string>
@@ -1275,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Преостало је још<xliff:g id="PERCENTAGE">%s</xliff:g> батерије"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерија писаљке је на <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Повежите писаљку са пуњачем"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Низак ниво батерије писаљке"</string>
     <string name="video_camera" msgid="7654002575156149298">"Видео камера"</string>
@@ -1327,6 +1352,15 @@
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пречице претраге"</string>
     <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
+    <skip />
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
+    <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 2b88829..6f2c07b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Tryck för att visa"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Det gick inte att spara skärminspelningen"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Det gick inte att starta skärminspelningen"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Probleminspelare"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Behandlar probleminspelning"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Avisering om pågående probleminsamlingssession"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Det gick inte att spara probleminspelning"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Det gick inte att starta probleminspelning"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Visar på fullskärm"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Svep nedåt från skärmens överkant för att avsluta."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Tillbaka"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Startsida"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koppla från"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivera"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som Snabbdelning och Hitta min enhet använder Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveras i morgon bitti"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ljuddelning"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Delar ljud"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Vilken enhetsupplevelse påverkades?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Välj problemtyp"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skärminspelning"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Prestanda"</item>
-    <item msgid="1627504621139124393">"Användargränssnitt"</item>
-    <item msgid="8309220355268900335">"Batteri"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Prestanda"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Användargränssnitt"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termisk"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörhjälpmedel"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiva"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frånkopplade"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörhjälpmedel"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parkoppla en ny enhet"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Knappen <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Start"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Tillbaka"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Uppåtpil"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Nedåtpil"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Vänsterpil"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Högerpil"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Centrera"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Blanksteg"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Retur"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tryck för mer information"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Inget inställt alarm"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ange skärmlåset"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeravtryckssensor"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentisera"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ange enhet"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"hopvikt"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"uppvikt"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> av batteriet återstår"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"E-pennans batteri: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Anslut e-pennan till en laddare"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"E-pennans batterinivå är låg"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Användes nyligen av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Används av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Användes nyligen av <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multikörning"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Ingång"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Genvägar till appar"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tillgänglighet"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 5260a5a..a15d975 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Gusa ili uangalie"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Hitilafu imetokea wakati wa kuhifadhi rekodi ya skrini"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Hitilafu imetokea wakati wa kuanza kurekodi skrini"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Kifaa cha Kurekodi Hitilafu"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Mchakato wa kurekodi hitilafu unaendelea"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Arifa inayoendelea kuhusu kipindi cha ukusanyaji wa data ya hitilafu"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Hitilafu imetokea wakati wa kuhifadhi rekodi ya hitilafu"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Hitilafu imetokea wakati wa kuanza kurekodi hitilafu"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Unatazama kwenye skrini nzima"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Ili uondoke, telezesha kidole kutoka juu hadi chini."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Nimeelewa"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Nyuma"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Nyumbani"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ondoa"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"anza kutumia"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Vipengele kama vile Kutuma Haraka na Tafuta Kifaa Changu hutumia Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth itawaka kesho asubuhi"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kusikiliza Pamoja"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Mnasikiliza Pamoja"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Utendaji"</item>
-    <item msgid="1627504621139124393">"Kiolesura cha Mtumiaji"</item>
-    <item msgid="8309220355268900335">"Betri"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Utendaji"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Kiolesura"</string>
+    <string name="thermal" msgid="6758074791325414831">"Joto"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Vifaa vya kusikilizia"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Vimeunganishwa"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Havijaunganishwa"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Vifaa vya kusikilizia"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Unganisha kifaa kipya"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Kitufe cha <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Mwanzo"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Nyuma"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Kishale cha juu"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Kishale cha chini"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Kishale cha kushoto"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Kishale cha kulia"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Katikati"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Gusa ili upate maelezo zaidi"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Hujaweka kengele"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"weka kifunga skrini"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Kitambua alama ya kidole"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"thibitisha"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"weka kifaa"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kimekunjwa"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kimefunguliwa"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Chaji ya betri imesalia <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Betri ya stylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Unganisha stylus yako kwenye chaja"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string>
     <string name="video_camera" msgid="7654002575156149298">"Kamera ya video"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Ilitumiwa hivi majuzi na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Inatumiwa na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ilitumiwa hivi majuzi na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Mfumo"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Majukumu mengi"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kifaa cha kuingiza data"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Njia za mikato za programu"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ufikivu"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 29e0dbe..4007bb1 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -26,6 +26,8 @@
     <dimen name="status_bar_icons_padding_start">3dp</dimen>
     <dimen name="status_bar_icons_padding_bottom">2dp</dimen>
     <dimen name="status_bar_icons_padding_top">2dp</dimen>
+    <dimen name="status_bar_battery_end_padding">4dp</dimen>
+
 
     <dimen name="status_bar_padding_end">0dp</dimen>
 
@@ -56,7 +58,7 @@
     <dimen name="navigation_key_padding">25dp</dimen>
 
     <!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
-    <dimen name="immersive_mode_cling_width">380dp</dimen>
+    <dimen name="immersive_mode_cling_width">600dp</dimen>
 
     <!-- Keyboard shortcuts helper -->
     <dimen name="ksh_layout_width">488dp</dimen>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 67a31af..c9e17ff 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"பார்க்கத் தட்டவும்"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"ஸ்கிரீன் ரெக்கார்டிங்கைச் சேமிப்பதில் பிழை ஏற்பட்டது"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ஸ்கிரீன் ரெக்கார்டிங்கைத் தொடங்குவதில் பிழை"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"சிக்கல் ரெக்கார்டர்"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"சிக்கல் ரெக்கார்டிங்கைச் செயலாக்குகிறது"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"சிக்கல் சேகரிப்பு அமர்வுக்கான பின்னணிச் செயல்பாட்டின் அறிவிப்பு"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"சிக்கல் தொடர்பான ரெக்கார்டிங்கைச் சேமிப்பதில் பிழை ஏற்பட்டது"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"சிக்கலை ரெக்கார்டிங் செய்யத் தொடங்குவதில் பிழை ஏற்பட்டது"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"முழுத் திரையில் காட்டுகிறது"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"வெளியேற, மேலிருந்து கீழே ஸ்வைப் செய்யவும்."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"சரி"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"பின்செல்"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"முகப்பு"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"இணைப்பு நீக்கும்"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"செயல்படுத்தும்"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படுதல்"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்கள் புளூடூத்தைப் பயன்படுத்துகின்றன"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ஆடியோ பகிர்வு"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ஆடியோ பகிரப்படுகிறது"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string>
@@ -305,7 +335,7 @@
     <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"நெட்வொர்க்குகள் கிடைக்கவில்லை"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"வைஃபை நெட்வொர்க்குகள் இல்லை"</string>
     <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"ஆன் செய்கிறது…"</string>
-    <string name="quick_settings_cast_title" msgid="3033553249449938182">"Cast"</string>
+    <string name="quick_settings_cast_title" msgid="3033553249449938182">"அலைபரப்பு"</string>
     <string name="quick_settings_casting" msgid="1435880708719268055">"அனுப்புகிறது"</string>
     <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"பெயரிடப்படாத சாதனம்"</string>
     <string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"சாதனங்கள் இல்லை"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"சாதன அனுபவத்தின் எந்தப் பகுதி பாதிக்கப்பட்டது?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"சிக்கல் வகையைத் தேர்வுசெய்க"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ஸ்கிரீன் ரெக்கார்டு"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"செயல்திறன்"</item>
-    <item msgid="1627504621139124393">"பயனர் இடைமுகம்"</item>
-    <item msgid="8309220355268900335">"பேட்டரி"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"செயல்திறன்"</string>
+    <string name="user_interface" msgid="3712869377953950887">"பயனர் இடைமுகம்"</string>
+    <string name="thermal" msgid="6758074791325414831">"தெர்மல்"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ஒற்றைக் கைப் பயன்முறை"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"செவித்துணைக் கருவிகள்"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"செயலில் உள்ளது"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"இணைக்கப்படவில்லை"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"செவித்துணைக் கருவிகள்"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"புதிய சாதனத்தை இணை"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"புதிய சாதனத்தை இணைக்க கிளிக் செய்யலாம்"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"முன்னமைவைப் புதுப்பிக்க முடியவில்லை"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"முன்னமைவு"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"உடனடி வசன உரை"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"சாதனத்தின் மைக்ரோஃபோனுக்கான தடுப்பை நீக்கவா?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"சாதனத்தின் கேமராவுக்கான தடுப்பை நீக்கவா?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"சாதனத்தின் கேமராவுக்கும் மைக்ரோஃபோனுக்குமான தடுப்பை நீக்கவா?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> பட்டன்"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"ஹோம்"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"பேக்"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"மேல்நோக்கிய அம்புக்குறி"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"கீழ்நோக்கிய அம்புக்குறி"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"இடது அம்புக்குறி"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"வலது அம்புக்குறி"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"நடு"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"ஸ்பேஸ்"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"என்டர்"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"மேலும் தகவல்களுக்கு தட்டவும்"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"அலாரம் எதுவுமில்லை"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"திரைப் பூட்டை உள்ளிடலாம்"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"கைரேகை சென்சார்"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"அங்கீகரி"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"சாதனத்தைத் திற"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"மடக்கப்பட்டது"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"விரிக்கப்பட்டது"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> பேட்டரி மீதமுள்ளது"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ஸ்டைலஸ் பேட்டரி <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"உங்கள் ஸ்டைலஸைச் சார்ஜருடன் இணையுங்கள்"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"ஸ்டைலஸின் பேட்டரி குறைவாக உள்ளது"</string>
     <string name="video_camera" msgid="7654002575156149298">"வீடியோ கேமரா"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ஆப்ஸால் சமீபத்தில் பயன்படுத்தப்பட்டது"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ஆப்ஸால் பயன்படுத்தப்படுகிறது"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ஆப்ஸால் சமீபத்தில் பயன்படுத்தப்பட்டது"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"சிஸ்டம்"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"பல வேலைகளைச் செய்தல்"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"உள்ளீடு"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ஆப்ஸ் ஷார்ட்கட்கள்"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"அணுகல்தன்மை"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"கீபோர்டு ஷார்ட்கட்கள்"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 6e10896..4917ff6 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"చూడటానికి ట్యాప్ చేయండి"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"స్క్రీన్ రికార్డింగ్‌ను సేవ్ చేయడంలో ఎర్రర్ ఏర్పడింది"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"స్క్రీన్ రికార్డింగ్ ప్రారంభించడంలో ఎర్రర్ ఏర్పడింది"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"సమస్య రికార్డర్"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"సమస్య రికార్డింగ్ ప్రాసెసింగ్"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"సమస్య సేకరణ సెషన్ కోసం కొనసాగుతోన్న నోటిఫికేషన్"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"సమస్య రికార్డింగ్‌ను సేవ్ చేయడంలో ఎర్రర్ ఏర్పడింది"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"సమస్య రికార్డింగ్‌ను ప్రారంభించడంలో ఎర్రర్ ఏర్పడింది"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"ఫుల్ స్క్రీన్‌లో చూస్తున్నారు"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"నిష్క్రమించడానికి, పై నుండి కిందికి స్వైప్ చేయండి."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"సరే"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"వెనుకకు"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"హోమ్"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"సేవ్ అయ్యింది"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"డిస్‌కనెక్ట్ చేయండి"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"యాక్టివేట్ చేయండి"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్‌గా ఆన్ చేస్తుంది"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"క్విక్ షేర్, Find My Device వంటి ఫీచర్‌లు బ్లూటూత్‌ను ఉపయోగిస్తాయి"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"బ్లూటూత్ రేపు ఉదయం ఆన్ అవుతుంది"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ఆడియో షేరింగ్"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ఆడియోను షేర్ చేస్తున్నారు"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"ఆడియోను షేర్ చేయండి"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"ఆడియోను షేర్ చేస్తున్నారు"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్‌సెట్"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"మీ పరికర అనుభవంలో ఏ భాగం ప్రభావితమైంది?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"సమస్య రకాన్ని ఎంచుకోండి"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"స్క్రీన్ రికార్డ్"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"పనితీరు"</item>
-    <item msgid="1627504621139124393">"యూజర్ ఇంటర్‌ఫేస్"</item>
-    <item msgid="8309220355268900335">"బ్యాటరీ"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"పనితీరు"</string>
+    <string name="user_interface" msgid="3712869377953950887">"యూజర్ ఇంటర్‌ఫేస్"</string>
+    <string name="thermal" msgid="6758074791325414831">"థర్మల్"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"వినికిడి పరికరాలు"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"యాక్టివ్"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"డిస్‌కనెక్ట్ అయ్యింది"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"వినికిడి పరికరాలు"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"కొత్త పరికరాన్ని పెయిర్ చేయడానికి క్లిక్ చేయండి"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ప్రీసెట్‌ను అప్‌డేట్ చేయడం సాధ్యపడలేదు"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ప్రీసెట్"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"లైవ్ క్యాప్షన్"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్‌ను అన్‌బ్లాక్ చేయమంటారా?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్‌బ్లాక్ చేయమంటారా?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్‌లను అన్‌బ్లాక్ చేయమంటారా?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"బటన్ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"వెనుకకు"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"పై వైపు బాణం"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"కింది వైపు బాణం"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ఎడమ వైపు బాణం"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"కుడి వైపు బాణం"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"మధ్య"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -989,7 +1011,7 @@
     <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"మాగ్నిఫికేషన్ సెట్టింగ్‌లను మూసివేయండి"</string>
     <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"సవరణ మోడ్ నుండి ఎగ్జిట్ అవ్వండి"</string>
     <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"సైజ్ మార్చడానికి మూలను లాగండి"</string>
-    <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"డయాగనల్ స్క్రోలింగ్‌ను అనుమతించండి"</string>
+    <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"డయాగోనల్ స్క్రోలింగ్‌ను అనుమతించండి"</string>
     <string name="accessibility_resize" msgid="5733759136600611551">"సైజ్ మార్చండి"</string>
     <string name="accessibility_change_magnification_type" msgid="666000085077432421">"మ్యాగ్నిఫికేషన్ రకాన్ని మార్చండి"</string>
     <string name="accessibility_magnification_end_resizing" msgid="4881690585800302628">"సైజ్‌ను మార్చడం ముగించండి"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"మరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"అలారం సెట్ చేయలేదు"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"స్క్రీన్ లాక్‌ను ఎంటర్ చేయండి"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"వేలిముద్ర సెన్సార్"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ప్రామాణీకరించండి"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"పరికరాన్ని ఎంటర్ చేయండి"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"మడిచే సదుపాయం గల పరికరం"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"మడిచే సదుపాయం లేని పరికరం"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> బ్యాటరీ మిగిలి ఉంది"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"స్టయిలస్ బ్యాటరీ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"మీ స్టైలస్‌ను ఛార్జర్‌కి కనెక్ట్ చేయండి"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టైలస్ బ్యాటరీ"</string>
     <string name="video_camera" msgid="7654002575156149298">"వీడియో కెమెరా"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ద్వారా ఇటీవల వినియోగించబడింది"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ద్వారా వినియోగంలో ఉంది"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ద్వారా ఇటీవల ఉపయోగించబడింది"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"సిస్టమ్"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"మల్టీ-టాస్కింగ్"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ఇన్‌పుట్"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"యాప్ షార్ట్‌కట్‌లు"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"యాక్సెసిబిలిటీ"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"కీబోర్డ్ షార్ట్‌కట్‌లు"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"సెర్చ్ షార్ట్‌కట్‌లు"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్‌లైట్"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 1f0f45a1..f4914f0 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"แตะเพื่อดู"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"เกิดข้อผิดพลาดในการบันทึกหน้าจอ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"เกิดข้อผิดพลาดขณะเริ่มบันทึกหน้าจอ"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"โปรแกรมบันทึกปัญหา"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"กำลังประมวลผลการบันทึกปัญหา"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการรวบรวมปัญหา"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"เกิดข้อผิดพลาดในการบันทึกไฟล์บันทึกปัญหา"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"เกิดข้อผิดพลาดในการเริ่มบันทึกปัญหา"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"กำลังดูแบบเต็มหน้าจอ"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"หากต้องการออก ให้ปัดลงจากด้านบน"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"รับทราบ"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"กลับ"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"หน้าแรก"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"บันทึกแล้ว"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"เปิดอีกครั้งโดยอัตโนมัติในวันพรุ่งนี้"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และหาอุปกรณ์ของฉัน ใช้บลูทูธ"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และ \"หาอุปกรณ์ของฉัน\" ใช้บลูทูธ"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"บลูทูธจะเปิดพรุ่งนี้เช้า"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"การแชร์เสียง"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"กำลังแชร์เสียง"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ประสบการณ์การใช้งานอุปกรณ์ส่วนใดที่ได้รับผลกระทบ"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"เลือกประเภทปัญหา"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"บันทึกหน้าจอ"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"ประสิทธิภาพ"</item>
-    <item msgid="1627504621139124393">"อินเทอร์เฟซผู้ใช้"</item>
-    <item msgid="8309220355268900335">"แบตเตอรี่"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"ประสิทธิภาพ"</string>
+    <string name="user_interface" msgid="3712869377953950887">"อินเทอร์เฟซผู้ใช้"</string>
+    <string name="thermal" msgid="6758074791325414831">"ความร้อน"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"โหมดมือเดียว"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"เครื่องช่วยฟัง"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ใช้งานอยู่"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ยกเลิกการเชื่อมต่อแล้ว"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"เครื่องช่วยฟัง"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"จับคู่อุปกรณ์ใหม่"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"คลิกเพื่อจับคู่อุปกรณ์ใหม่"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ไม่สามารถอัปเดตค่าที่กำหนดล่วงหน้า"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ค่าที่กำหนดล่วงหน้า"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"คำบรรยายสด"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"เลิกบล็อกไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"เลิกบล็อกกล้องของอุปกรณ์ใช่ไหม"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"เลิกบล็อกกล้องและไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ปุ่ม <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"กลับ"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"ลูกศรขึ้น"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"ลูกศรลง"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"ลูกศรซ้าย"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"ลูกศรขวา"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"กึ่งกลาง"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"วรรค"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"แตะดูข้อมูลเพิ่มเติม"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ไม่มีการตั้งปลุก"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ป้อนข้อมูลการล็อกหน้าจอ"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"เซ็นเซอร์ลายนิ้วมือ"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ตรวจสอบสิทธิ์"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"เข้าถึงอุปกรณ์"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"พับ"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"กางออก"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"เหลือแบตเตอรี่ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"แบตเตอรี่สไตลัส <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"เชื่อมต่อสไตลัสกับที่ชาร์จ"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"แบตเตอรี่สไตลัสเหลือน้อย"</string>
     <string name="video_camera" msgid="7654002575156149298">"กล้องวิดีโอ"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ใช้ล่าสุดโดย <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"ใช้อยู่โดย <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ใช้ล่าสุดโดย <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"ระบบ"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"การทํางานหลายอย่างพร้อมกัน"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"อินพุต"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"แป้นพิมพ์ลัดของแอป"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"การช่วยเหลือพิเศษ"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"แป้นพิมพ์ลัด"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index d3517d4..89fa5ea 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"I-tap para tingnan"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Nagka-error sa pag-save ng recording ng screen"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Nagkaroon ng error sa pagsisimula ng pag-record ng screen"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Recorder ng Isyu"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Pinoproseso: recording ng isyu"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Kasalukuyang notification para sa session ng pangongolekta ng isyu"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Nagkaroon ng error sa pag-save ng recording ng isyu"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Nagkaroon ng error sa pagsisimula ng pag-record ng isyu"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Nanonood sa full screen"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Para lumabas, mag-swipe mula sa itaas pababa."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Bumalik"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Home"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Na-save"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"idiskonekta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"i-activate"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Gumagamit ng Bluetooth ang mga feature tulad ng Quick Share at Hanapin ang Aking Device"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Mag-o-on ang Bluetooth bukas ng umaga"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Pag-share ng Audio"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ibinabahagi ang Audio"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ano\'ng naapektuhang parte ng experience sa device?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Piliin ang uri ng isyu"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pag-record ng screen"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performance"</item>
-    <item msgid="1627504621139124393">"User Interface"</item>
-    <item msgid="8309220355268900335">"Baterya"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performance"</string>
+    <string name="user_interface" msgid="3712869377953950887">"User Interface"</string>
+    <string name="thermal" msgid="6758074791325414831">"Thermal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Mga hearing device"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nadiskonekta"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Mga hearing device"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Magpares ng bagong device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hindi ma-update ang preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Instant Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"I-unblock ang mikropono ng device?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"I-unblock ang camera ng device?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"I-unblock ang camera at mikropono ng device?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Button na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Back"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Pataas na arrow"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pababang arrow"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Pakaliwang arrow"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Pakanang arrow"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Center"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"I-tap para sa higit pang impormasyon"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Walang alarm"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ilagay ang lock ng screen"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor para sa fingerprint"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"i-authenticate"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ilagay ang device"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"naka-fold"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"hindi naka-fold"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> baterya na lang ang natitira"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterya ng stylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ikonekta sa charger ang iyong stylus"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Paubos na ang baterya ng stylus"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video camera"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kamakailang ginamit ng <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Ginagamit ng <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kamakailang ginamit ng <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"System"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Pag-multitask"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Input"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Mga shortcut ng app"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index b54fd95..bf6e6aa 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Görüntülemek için dokunun"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Ekran kaydı saklanırken hata oluştu"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ekran kaydı başlatılırken hata oluştu"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Sorun Kaydedici"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Sorun kaydı işleniyor"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Sorun toplama oturumuyla ilgili devam eden görev bildirimi"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Sorun kaydı saklanırken hata oluştu"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Sorun kaydı başlatılırken hata oluştu"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Tam ekran olarak görüntüleme"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Çıkmak için yukarıdan aşağıya doğru hızlıca kaydırın."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Anladım"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Geri"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Ana sayfa"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"bağlantıyı kes"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"etkinleştir"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ve Cihazımı Bul gibi özellikler Bluetooth\'u kullanır"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth yarın sabah açılacak"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ses Paylaşımı"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ses paylaşılıyor"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne şekilde etkilendi?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Performans"</item>
-    <item msgid="1627504621139124393">"Kullanıcı Arayüzü"</item>
-    <item msgid="8309220355268900335">"Pil"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Performans"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Kullanıcı Arayüzü"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"İşitme cihazları"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Etkin"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlı değil"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"İşitme cihazları"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz eşle"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> düğmesi"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Geri"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Yukarı ok"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Aşağı ok"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Sol ok"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Sağ ok"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Orta"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Boşluk"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Daha fazla bilgi için dokunun"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Alarm yok"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ekran kilidini gir"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Parmak izi sensörü"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"kimlik doğrulaması yapın"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz girin"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"katlanmış"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"katlanmamış"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> pil kaldı"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ekran kalemi pili: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ekran kaleminizi bir şarj cihazına bağlayın"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Ekran kaleminin pil seviyesi düşük"</string>
     <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"En son <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) tarafından kullanıldı"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) tarafından kullanılıyor"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"En son <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) tarafından kullanıldı"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Sistem"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Çoklu görev becerisi"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Giriş"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Uygulama kısayolları"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erişilebilirlik"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 4768dcb..6206289 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Натисніть, щоб переглянути"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Не вдалося зберегти запис відео з екрана"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Не вдалося почати запис екрана"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Засіб запису проблем"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Обробка запису проблеми"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Поточне сповіщення про сеанс збирання даних про проблему"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Не вдалося зберегти запис проблеми"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Не вдалося почати запис проблеми"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Перегляд на весь екран"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Щоб вийти, проведіть пальцем униз від верху екрана."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Назад"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Головна"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Збережено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"від’єднати"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активувати"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Такі функції, як швидкий обмін і \"Знайти пристрій\", використовують Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth увімкнеться завтра вранці"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Надсилання аудіо"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Надсилання аудіо"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На який аспект роботи пристрою вплинула проблема?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Виберіть тип проблеми"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис відео з екрана"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Продуктивність"</item>
-    <item msgid="1627504621139124393">"Інтерфейс користувача"</item>
-    <item msgid="8309220355268900335">"Акумулятор"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Продуктивність"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Інтерфейс користувача"</string>
+    <string name="thermal" msgid="6758074791325414831">"Нагрівання"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухові апарати"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Під’єднано"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Від’єднано"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухові апарати"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Підключити новий пристрій"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не вдалось оновити набір налаштувань"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набір налаштувань"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Живі субтитри"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Надати доступ до мікрофона?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Надати доступ до камери пристрою?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Надати доступ до камери й мікрофона?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Кнопка <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Назад"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Стрілка вгору"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Стрілка вниз"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Стрілка вліво"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Стрілка вправо"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Центр"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Пробіл"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Натисніть, щоб дізнатися більше"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Немає будильників"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"показати способи розблокування екрана"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер відбитків пальців"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"пройти автентифікацію"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"відкрити пристрій"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складений"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"розкладений"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Заряд акумулятора: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Заряд акумулятора стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Підключіть стилус до зарядного пристрою"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Низький заряд акумулятора стилуса"</string>
     <string name="video_camera" msgid="7654002575156149298">"Відеокамера"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Нещодавно використано (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Використовується додатком <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Нещодавно використано (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Система"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Багатозадачність"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Введення"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Комбінації клавіш для додатків"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Доступність"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Комбінації клавіш"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 9410327..0aab4ce 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"دیکھنے کے لیے تھپتھپائیں"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"اسکرین ریکارڈنگ محفوظ کرنے میں خرابی"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"اسکرین ریکارڈنگ شروع کرنے میں خرابی"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"ایشو ریکارڈر"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ایشو ریکارڈنگ پروسیس ہو رہی ہے"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ایشو کلیکشن سیشن کے لیے جاری اطلاع"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ایشو ریکارڈنگ محفوظ کرنے میں خرابی"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ایشو ریکارڈنگ شروع کرنے میں خرابی"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"فُل اسکرین میں دیکھ رہے ہیں"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"باہر نکلنے کیلئے اوپر سے نیچے کی طرف سوائپ کریں۔"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"سمجھ آ گئی"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"واپس جائیں"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"ہوم"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"فوری اشتراک اور \'میرا آلہ ڈھونڈیں\' جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوٹوتھ کل صبح آن ہو جائے گا"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"آڈیو کا اشتراک"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"آڈیو کا اشتراک ہو رہا ہے"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"آڈیو کا اشتراک کریں"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"آڈیو کا اشتراک ہو رہا ہے"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"آپ کے آلے کے تجربے کا کون سا حصہ متاثر ہوا؟"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"مسئلہ کی قسم منتخب کریں"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"اسکرین ریکارڈ"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"کارکردگی"</item>
-    <item msgid="1627504621139124393">"یوزر انٹرفیس"</item>
-    <item msgid="8309220355268900335">"بیٹری"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"کارکردگی"</string>
+    <string name="user_interface" msgid="3712869377953950887">"یوزر انٹرفیس"</string>
+    <string name="thermal" msgid="6758074791325414831">"تھرمل"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ایک ہاتھ کی وضع"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعت کے آلات"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غیر منسلک ہے"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعت کے آلات"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"نئے آلے کا جوڑا بنائیں"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"نئے آلے کا جوڑا بنانے کے لیے کلک کریں"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"پہلے سے ترتیب شدہ کو اپ ڈیٹ نہیں کیا جا سکا"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پہلے سے ترتیب شدہ"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"لائیو کیپشن"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"آلے کا مائیکروفون غیر مسدود کریں؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"آلے کا کیمرا غیر مسدود کریں؟"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"آلے کا کیمرا اور مائیکروفون غیر مسدود کریں؟"</string>
@@ -732,11 +759,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"بٹن <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"پیچھے"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"اوپر تیر کا نشان"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"نیچے تیر کا نشان"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"بایاں تیر"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"دایاں تیر"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"سینٹر"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1183,6 +1205,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"مزید معلومات کے لیے تھپتھپائیں"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"کوئی الارم سیٹ نہیں ہے"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"اسکرین لاک درج کریں"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"فنگر پرنٹ سینسر"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"تصدیق کریں"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"آلہ درج کریں"</string>
@@ -1280,7 +1304,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"فولڈ کردہ"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"اَن فولڈ کردہ"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> بیٹری باقی ہے"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"<xliff:g id="PERCENTAGE">%s</xliff:g> اسٹائلس بیٹری"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"اپنے اسٹائلس کو چارجر منسلک کریں"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"اسٹائلس بیٹری کم ہے"</string>
     <string name="video_camera" msgid="7654002575156149298">"ویڈیو کیمرا"</string>
@@ -1323,23 +1347,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) کے ذریعے حال ہی میں استعمال کیا گیا"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) کے زیر استعمال"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) کے ذریعے حال ہی میں استعمال کیا گیا"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"سسٹم"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ملٹی ٹاسکنگ"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"ان پٹ"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"ایپ شارٹ کٹس"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ایکسیسبیلٹی"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"کی بورڈ شارٹ کٹس"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"‏%2$d میں سے ‎%1$d کا لیول"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 8ad7e14..312737d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Koʻrish uchun bosing"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Ekran yozuvi saqlanmadi"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ekranni yozib olish boshlanmadi"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Muammoni yozib olish vositasi"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Yozuv qayta ishlanmoqda"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Muammo toʻplash seansi uchun faol bildirishnoma"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Yozuv saqlanmadi"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Yozib olish boshlanmadi"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Butun ekran rejimi"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Chiqish uchun tepadan pastga torting."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"OK"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Orqaga"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Uyga"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"uzish"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"faollashtirish"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tezkor ulashuv va Qurilmamni top kabi funksiyalar Bluetooth ishlatadi"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ertaga ertalab yoqiladi"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio ulashuvi"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio ulashuvi yoniq"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Qurilma ishlashining qaysi qismiga taʼsir qildi?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Muammo turini tanlang"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran yozuvi"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Unumdorlik"</item>
-    <item msgid="1627504621139124393">"Foydalanuvchi interfeysi"</item>
-    <item msgid="8309220355268900335">"Batareya"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Unumdorlik"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Foydalanuvchi interfeysi"</string>
+    <string name="thermal" msgid="6758074791325414831">"Termal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eshitish qurilmalari"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Faol"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Uzildi"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eshitish qurilmalari"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yangi qurilmani ulash"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> tugmasi"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Bosh ekran"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Orqaga"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Tepaga strelka"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Pastga strelka"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Chapga strelka"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Oʻngga strelka"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Markaziy ko‘rsatkichli chiziq"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Probel"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Batafsil axborot olish uchun bosing"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Signal sozlanmagan"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"ekran qulfini kiriting"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Barmoq izi skaneri"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifikatsiya"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"qurilmani ochish"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"buklangan"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"buklanmagan"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Batareya quvvati: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilus batareyasi: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Stilusni quvvat manbaiga ulang"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus batareyasi kam"</string>
     <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Yaqinda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ishlatgan"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ishlatmoqda"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Yaqinda <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) ishlatgan"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Tizim"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multi-vazifalilik"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Kiritish"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Ilova yorliqlari"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qulayliklar"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 1efa983..f578e14a 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Nhấn để xem"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Có lỗi xảy ra khi lưu video ghi màn hình"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Lỗi khi bắt đầu ghi màn hình"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Trình ghi sự cố"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Đang xử lý bản ghi sự cố"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Thông báo hiển thị liên tục cho một phiên thu thập sự cố"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Đã xảy ra lỗi khi lưu bản ghi sự cố"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Đã xảy ra lỗi khi bắt đầu ghi sự cố"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Xem toàn màn hình"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Để thoát, hãy vuốt từ trên cùng xuống dưới."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Tôi hiểu"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Quay lại"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Trang chủ"</string>
@@ -247,7 +274,7 @@
     <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Không làm phiền."</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"Bluetooth."</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"Bluetooth bật."</string>
-    <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Báo thức được đặt cho <xliff:g id="TIME">%s</xliff:g>."</string>
+    <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"Chuông báo được đặt cho <xliff:g id="TIME">%s</xliff:g>."</string>
     <string name="accessibility_quick_settings_more_time" msgid="7646479831704665284">"Nhiều thời gian hơn."</string>
     <string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Ít thời gian hơn."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Đã ngừng truyền màn hình."</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ngắt kết nối"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"kích hoạt"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Các tính năng như Chia sẻ nhanh và Tìm thiết bị của tôi đều sử dụng Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sẽ bật vào sáng mai"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Chia sẻ âm thanh"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Đang chia sẻ âm thanh"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string>
@@ -361,13 +391,13 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Hiệu suất"</item>
-    <item msgid="1627504621139124393">"Giao diện người dùng"</item>
-    <item msgid="8309220355268900335">"Pin"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Hiệu suất"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Giao diện người dùng"</string>
+    <string name="thermal" msgid="6758074791325414831">"Nhiệt"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Chế độ một tay"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Thiết bị trợ thính"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Đang hoạt động"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Đã ngắt kết nối"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Thiết bị trợ thính"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Ghép nối thiết bị mới"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string>
@@ -594,11 +624,11 @@
     <string name="screen_pinning_negative" msgid="6882816864569211666">"Không, cảm ơn"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"Đã ghim ứng dụng"</string>
     <string name="screen_pinning_exit" msgid="4553787518387346893">"Đã bỏ ghim ứng dụng"</string>
-    <string name="stream_voice_call" msgid="7468348170702375660">"Gọi"</string>
+    <string name="stream_voice_call" msgid="7468348170702375660">"Cuộc gọi"</string>
     <string name="stream_system" msgid="7663148785370565134">"Hệ thống"</string>
     <string name="stream_ring" msgid="7550670036738697526">"Chuông"</string>
     <string name="stream_music" msgid="2188224742361847580">"Nội dung nghe nhìn"</string>
-    <string name="stream_alarm" msgid="16058075093011694">"Báo thức"</string>
+    <string name="stream_alarm" msgid="16058075093011694">"Chuông báo"</string>
     <string name="stream_notification" msgid="7930294049046243939">"Thông báo"</string>
     <string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="7322536356554673067">"Tần số đa chuông kép"</string>
@@ -642,7 +672,7 @@
     <string name="enable_demo_mode" msgid="3180345364745966431">"Bật chế độ trình diễn"</string>
     <string name="show_demo_mode" msgid="3677956462273059726">"Hiển thị chế độ trình diễn"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
-    <string name="status_bar_alarm" msgid="87160847643623352">"Báo thức"</string>
+    <string name="status_bar_alarm" msgid="87160847643623352">"Chuông báo"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Ví"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Thiết lập để mua hàng nhanh hơn và an toàn hơn bằng điện thoại"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Hiện tất cả"</string>
@@ -655,7 +685,7 @@
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Đang cập nhật"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Hồ sơ công việc"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Chế độ trên máy bay"</string>
-    <string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy báo thức tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string>
+    <string name="zen_alarm_warning" msgid="7844303238486849503">"Bạn sẽ không nghe thấy chuông báo tiếp theo lúc <xliff:g id="WHEN">%1$s</xliff:g> của mình"</string>
     <string name="alarm_template" msgid="2234991538018805736">"lúc <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"vào <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Điểm phát sóng"</string>
@@ -727,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Nút <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Quay lại"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Mũi tên lên"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Mũi tên xuống"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Mũi tên trái"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Mũi tên phải"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Giữa"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Dấu cách"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -933,7 +958,7 @@
     <string name="running_foreground_services_title" msgid="5137313173431186685">"Ứng dụng đang chạy trong nền"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"Tắt dữ liệu di động?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua chế độ <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua mạng <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"nhà mạng của bạn"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Chuyển về <xliff:g id="CARRIER">%s</xliff:g>?"</string>
     <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Dữ liệu di động sẽ không tự động chuyển dựa trên tình trạng phủ sóng"</string>
@@ -1178,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Nhấn để biết thêm thông tin"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Chưa đặt chuông báo"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"nhập phương thức mở khoá màn hình"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Cảm biến vân tay"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"xác thực"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"truy cập thiết bị"</string>
@@ -1275,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gập"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"mở"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"Còn <xliff:g id="PERCENTAGE">%s</xliff:g> pin"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Mức pin bút cảm ứng <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hãy kết nối bút cảm ứng với bộ sạc"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Bút cảm ứng bị yếu pin"</string>
     <string name="video_camera" msgid="7654002575156149298">"Máy quay video"</string>
@@ -1318,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) đã dùng gần đây"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) đang dùng"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) đã dùng gần đây"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Hệ thống"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Đa nhiệm"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Phương thức nhập"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Lối tắt ứng dụng"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hỗ trợ tiếp cận"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 0860f31..ae1f2b0 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"点按即可查看"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"保存屏幕录制内容时出错"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"启动屏幕录制时出错"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"问题录制器"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"正在处理问题录制"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"针对问题收集会话的持续性通知"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"保存问题录制内容时出错"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"启动问题录制时出错"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"目前处于全屏模式"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"要退出,请从顶部向下滑动。"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"知道了"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"返回"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"主屏幕"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已保存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"断开连接"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"启用"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"快速分享、查找我的设备等功能会使用蓝牙"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"蓝牙将在明天早上开启"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音频分享"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音频"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音频"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音频"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string>
@@ -361,25 +389,24 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"设备体验的哪个方面受到影响?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"选择问题类型"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"屏幕录制"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"性能"</item>
-    <item msgid="1627504621139124393">"界面"</item>
-    <item msgid="8309220355268900335">"电池"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"性能"</string>
+    <string name="user_interface" msgid="3712869377953950887">"界面"</string>
+    <string name="thermal" msgid="6758074791325414831">"散热"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"单手模式"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助听装置"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已连接"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已断开连接"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助听装置"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"与新设备配对"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"点击即可与新设备配对"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"无法更新预设"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"预设"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"实时字幕"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解锁设备麦克风吗?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解锁设备摄像头吗?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解锁设备摄像头和麦克风吗?"</string>
-    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"这将会为所有获准使用您麦克风的应用和服务启用这项权限。"</string>
-    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"这将会为所有获准使用您摄像头的应用和服务启用这项权限。"</string>
+    <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"解锁后,所有具有麦克风使用权的应用和服务都可使用您的麦克风。"</string>
+    <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"解锁后,所有具有摄像头使用权的应用和服务都可使用您的摄像头。"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"这将会为所有获准使用您的摄像头或麦克风的应用和服务启用这项权限。"</string>
     <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"麦克风已被屏蔽"</string>
     <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"摄像头已被屏蔽"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g>按钮"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上键"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下键"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左键"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右键"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中心"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"空格"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
@@ -934,7 +956,7 @@
     <string name="running_foreground_services_title" msgid="5137313173431186685">"在后台运行的应用"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"点按即可详细了解电量和流量消耗情况"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"要关闭移动数据网络吗?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"您将无法通过<xliff:g id="CARRIER">%s</xliff:g>使用移动数据或互联网,只能通过 WLAN 连接到互联网。"</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"关闭后,您将无法通过<xliff:g id="CARRIER">%s</xliff:g>使用移动数据或互联网,只能通过 WLAN 连接到互联网。"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"您的运营商"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"是否要切换回 <xliff:g id="CARRIER">%s</xliff:g>?"</string>
     <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"移动流量不会根据网络可用情况自动切换"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"点按即可了解详情"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未设置闹钟"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"输入屏幕解锁信息"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指纹传感器"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"身份验证"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"进入设备"</string>
@@ -1208,7 +1232,7 @@
     <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"添加功能块"</string>
     <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"不添加功能块"</string>
     <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"选择用户"</string>
-    <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 个应用处于活动状态}other{# 个应用处于活动状态}}"</string>
+    <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 个应用处于运行状态}other{# 个应用处于运行状态}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"新信息"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"已开启的应用"</string>
     <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"这些应用即使在闲置时仍处于活跃运行状态。应用的功能会因此提升,但可能会影响电池续航时间。"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折叠状态"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"展开状态"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"电池还剩 <xliff:g id="PERCENTAGE">%s</xliff:g> 的电量"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"触控笔电量为 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"请将触控笔连接充电器"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"触控笔电池电量低"</string>
     <string name="video_camera" msgid="7654002575156149298">"摄像机"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”最近使用过(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正在使用(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”最近使用过(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"系统"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多任务处理"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"输入"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"应用快捷键"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"无障碍功能"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"键盘快捷键"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 39c1b2b..35087f3 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"輕按即可查看"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"儲存螢幕錄影時發生錯誤"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄影畫面時發生錯誤"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"問題記錄工具"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"正在處理問題記錄"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"問題收集工作階段的持續通知"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"儲存錄影問題時發生錯誤"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"開始記錄問題時發生錯誤"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"以全螢幕檢視"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"如要退出,請從螢幕頂部向下滑動。"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"知道了"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"返回"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"首頁"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"解除連結"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟動"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速共享」和「尋找我的裝置」等功能都會使用藍牙"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙將於明天上午開啟"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享功能"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -361,20 +389,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受影響?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"效能"</item>
-    <item msgid="1627504621139124393">"使用者介面"</item>
-    <item msgid="8309220355268900335">"電池"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"效能"</string>
+    <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string>
+    <string name="thermal" msgid="6758074791325414831">"熱能"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已中斷連線"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"㩒一下就可以配對新裝置"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解除封鎖裝置相機和麥克風嗎?"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 鍵"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上箭咀"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下箭咀"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左箭咀"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右箭咀"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"箭咀中央"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"空格"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"輸入 (Enter)"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"輕按即可瞭解詳情"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未設定鬧鐘"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"輸入螢幕鎖定憑證"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋感應器"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"驗證"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已打開"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"剩餘電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆連接充電器"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電量不足"</string>
     <string name="video_camera" msgid="7654002575156149298">"攝影機"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近使用過此權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> 正在使用 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近使用過此權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"系統"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"無障礙功能"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index f1a6f81..51fe3d7 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"輕觸即可查看"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"儲存螢幕錄影內容時發生錯誤"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄製螢幕畫面時發生錯誤"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"問題記錄工具"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"正在處理問題記錄"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"問題收集工作階段的持續性通知"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"儲存問題記錄時發生錯誤"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"開始記錄問題時發生錯誤"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"以全螢幕檢視"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"如要退出,請從螢幕頂端向下滑動。"</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"我知道了"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"返回"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"主畫面"</string>
@@ -278,11 +305,12 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"取消連結"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟用"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都需要使用藍牙技術"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"分享音訊"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"正在分享音訊"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string>
@@ -361,22 +389,21 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受到影響?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"效能"</item>
-    <item msgid="1627504621139124393">"使用者介面"</item>
-    <item msgid="8309220355268900335">"電池"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"效能"</string>
+    <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string>
+    <string name="thermal" msgid="6758074791325414831">"熱成像"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"連線中斷"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"按一下即可配對新裝置"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設設定"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
-    <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
+    <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string>
     <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"執行後,具備麥克風存取權的所有應用程式和服務,都將可使用麥克風。"</string>
     <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"執行後,具備相機存取權的所有應用程式和服務,都將可使用相機。"</string>
@@ -728,11 +755,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"<xliff:g id="NAME">%1$s</xliff:g> 按鈕"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Home 鍵"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"返回"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"向上箭頭"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"向下箭頭"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"向左箭頭"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"向右箭頭"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"中央鍵"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"空格鍵"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter 鍵"</string>
@@ -934,7 +956,7 @@
     <string name="running_foreground_services_title" msgid="5137313173431186685">"在背景執行的應用程式"</string>
     <string name="running_foreground_services_msg" msgid="3009459259222695385">"輕觸即可查看電池和數據用量詳情"</string>
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉行動數據嗎?"</string>
-    <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路。你只能透過 Wi-Fi 使用網際網路。"</string>
+    <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路,只能利用 Wi-Fi 上網。"</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"你的電信業者"</string>
     <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"要切換回「<xliff:g id="CARRIER">%s</xliff:g>」嗎?"</string>
     <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"行動數據不會依據可用性自動切換"</string>
@@ -1179,6 +1201,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"輕觸即可瞭解詳情"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未設定鬧鐘"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"輸入螢幕鎖定憑證"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋感應器"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"驗證"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
@@ -1211,7 +1235,7 @@
     <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 個應用程式正在運作}other{# 個應用程式正在運作}}"</string>
     <string name="fgs_dot_content_description" msgid="2865071539464777240">"新資訊"</string>
     <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"運作中的應用程式"</string>
-    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式會在沒有使用的情況下持續運作。應用程式的實用度會因此提升,但也可能影響電池續航力。"</string>
+    <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式即使是在閒置狀態下,也會持續運作。應用程式的功能會因而提升,但也可能影響電池續航力。"</string>
     <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string>
     <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string>
     <string name="clipboard_edit_text_done" msgid="4551887727694022409">"完成"</string>
@@ -1276,7 +1300,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已展開"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"剩餘電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆接上充電器"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電力不足"</string>
     <string name="video_camera" msgid="7654002575156149298">"攝影機"</string>
@@ -1319,23 +1343,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近用過這項權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"正由「<xliff:g id="APP_NAME">%1$s</xliff:g>」使用 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」最近用過這項權限 (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"系統"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"多工處理"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"輸入"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"應用程式捷徑"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"無障礙"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 840d8a6..7b39f16 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -125,6 +125,32 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Thepha ukuze ubuke"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Iphutha lokulondoloza okokuqopha iskrini"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Iphutha lokuqala ukurekhoda isikrini"</string>
+    <!-- no translation found for screenrecord_stop_dialog_title (2685522129492260887) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message (1926783607059442889) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_message_specific_app (5285148796772616326) -->
+    <skip />
+    <!-- no translation found for screenrecord_stop_dialog_button (2883812564938194350) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_title (9212915050910250438) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message (3181723638915877339) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_message_specific_app (124371406810544777) -->
+    <skip />
+    <!-- no translation found for share_to_app_stop_dialog_button (6334056916284230217) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_title (1910372600290258193) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message (1502520537030715412) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_message_specific_app (4891536209254041850) -->
+    <skip />
+    <!-- no translation found for cast_to_other_device_stop_dialog_button (6420183747435521834) -->
+    <skip />
+    <!-- no translation found for close_dialog_button (4749497706540104133) -->
+    <skip />
     <string name="issuerecord_title" msgid="286627115110121849">"Ushicilelo Lokurekhoda"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Icubungula ushicilelo lokurekhoda"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Isaziso esiqhubekayo seqoqo leseshini yoshicilelo"</string>
@@ -135,7 +161,8 @@
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Iphutha ekulondolozeni ushicilelo lokurekhoda"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Iphutha lokuqalisa ushicilelo lokurekhoda"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Ukubuka isikrini esigcwele"</string>
-    <string name="immersive_cling_description" msgid="6913958856085185775">"Ukuze uphume, swayiphela phansi kusuka phezulu."</string>
+    <!-- no translation found for immersive_cling_description (2717426731830851921) -->
+    <skip />
     <string name="immersive_cling_positive" msgid="3076681691468978568">"Ngiyezwa"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Emuva"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"Ekhaya"</string>
@@ -278,11 +305,14 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"nqamula"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string>
+    <!-- no translation found for turn_on_bluetooth_auto_tomorrow (3345758139235739006) -->
+    <skip />
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Izakhi ezifana nokuthi Ukwabelana Ngokushesha kanye nokuthi Thola Idivayisi Yami zisebenzisa i-Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ukwabelana Ngokuqoshiwe"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ukwabelana Ngomsindo"</string>
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) -->
+    <skip />
+    <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) -->
+    <skip />
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string>
@@ -361,20 +391,19 @@
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string>
-  <string-array name="qs_record_issue_types">
-    <item msgid="2947988124014085798">"Ukusebenza"</item>
-    <item msgid="1627504621139124393">"Okusetshenziswa Kubonwa"</item>
-    <item msgid="8309220355268900335">"Ibhethri"</item>
-  </string-array>
+    <string name="performance" msgid="6552785217174378320">"Ukusebenza"</string>
+    <string name="user_interface" msgid="3712869377953950887">"Okusetshenziswa Kubonwa"</string>
+    <string name="thermal" msgid="6758074791325414831">"Ithermal"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Izinsizakuzwa"</string>
+    <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Kuyasebenza"</string>
+    <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Inqamukile"</string>
     <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Izinsizakuzwa"</string>
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bhangqa idivayisi entsha"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ayikwazanga ukubuyekeza ukusetha ngaphambilini"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ukusetha ngaphambilini"</string>
-    <!-- no translation found for live_caption_title (8916875614623730005) -->
-    <skip />
+    <string name="live_caption_title" msgid="8916875614623730005">"Amagama-ncazo abukhoma"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vulela imakrofoni yedivayisi?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vulela ikhamera yedivayisi?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vulela ikhamera yedivayisi nemakrofoni?"</string>
@@ -728,11 +757,6 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Inkinobho <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Ekhaya"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Emuva"</string>
-    <string name="keyboard_key_dpad_up" msgid="7199805608386368673">"Umcibisholo waphezulu"</string>
-    <string name="keyboard_key_dpad_down" msgid="3354221123220737397">"Umcibisholo waphansi"</string>
-    <string name="keyboard_key_dpad_left" msgid="144176368026538621">"Umcibisholo wangokwesokunxele"</string>
-    <string name="keyboard_key_dpad_right" msgid="8485763312139820037">"Umcibisholo wangokwesokudla"</string>
-    <string name="keyboard_key_dpad_center" msgid="4079412840715672825">"Maphakathi"</string>
     <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Isikhala"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Faka"</string>
@@ -1179,6 +1203,8 @@
     <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Thepha ukuze uthole olunye ulwazi"</string>
     <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Akukho alamu esethiwe"</string>
     <string name="accessibility_bouncer" msgid="5896923685673320070">"faka ukukhiya isikrini"</string>
+    <!-- no translation found for accessibility_side_fingerprint_indicator_label (1673807833352363712) -->
+    <skip />
     <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Inzwa yesigxivizo somunwe"</string>
     <string name="accessibility_authenticate_hint" msgid="798914151813205721">"gunyaza"</string>
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"faka idivayisi"</string>
@@ -1276,7 +1302,7 @@
     <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kugoqiwe"</string>
     <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kuvuliwe"</string>
     <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string>
-    <string name="stylus_battery_low_percentage" msgid="1620068112350141558">"<xliff:g id="PERCENTAGE">%s</xliff:g> ibhethri elisele"</string>
+    <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ibhethri lestylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Xhuma i-stylus yakho kushaja"</string>
     <string name="stylus_battery_low" msgid="7134370101603167096">"Ibhethri le-stylus liphansi"</string>
     <string name="video_camera" msgid="7654002575156149298">"Ikhamera yevidiyo"</string>
@@ -1319,23 +1345,23 @@
     <string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Kusetshenziswe kamuva yi-<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Isetshenziswa yi-<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Kusetshenziswe kamuva yi-<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
-    <!-- no translation found for shortcut_helper_category_system (462110876978937359) -->
+    <string name="shortcut_helper_category_system" msgid="462110876978937359">"Isistimu"</string>
+    <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Ukwenza imisebenzi eminingi"</string>
+    <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Okokufaka"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Izinqamuleli Zohlelo lokusebenza"</string>
+    <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ukufinyeleleka"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
+    <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 touchpad_tutorial_back_gesture_button (2746834288077265946) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_multitasking (7413381961404090136) -->
+    <!-- no translation found for touchpad_tutorial_home_gesture_button (7640544867625955304) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_input (8674018654124839566) -->
+    <!-- no translation found for touchpad_tutorial_action_key_button (3220074511852927267) -->
     <skip />
-    <!-- no translation found for shortcut_helper_category_app_shortcuts (8010249408308587117) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_category_a11y (6314444792641773464) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_title (8567500639300970049) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_search_placeholder (5488547526269871819) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_collapse_icon (8028015738431664954) -->
-    <skip />
-    <!-- no translation found for shortcut_helper_content_description_expand_icon (1084435697860417390) -->
+    <!-- no translation found for touchpad_tutorial_done_button (176168488821755503) -->
     <skip />
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fb88364..f8762f0 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -37,6 +37,9 @@
         <item>400</item>
     </integer-array>
 
+    <!-- Whether to use deadzone with nav bar -->
+    <bool name="config_useDeadZone">true</bool>
+
     <!-- decay duration (from size_max -> size), in ms -->
     <integer name="navigation_bar_deadzone_hold">333</integer>
     <integer name="navigation_bar_deadzone_decay">333</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d308c3d..40bdc3e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -810,6 +810,8 @@
     <dimen name="keyguard_smartspace_top_offset">12dp</dimen>
     <!-- The amount to translate lockscreen elements on the GONE->AOD transition -->
     <dimen name="keyguard_enter_from_top_translation_y">-100dp</dimen>
+    <!-- The amount to translate lockscreen elements on the GONE->AOD transition, on device fold -->
+    <dimen name="keyguard_enter_from_side_translation_x">-100dp</dimen>
 
     <dimen name="notification_scrim_corner_radius">32dp</dimen>
 
@@ -912,6 +914,10 @@
         obvious when corner radii differ.-->
     <dimen name="communal_enforced_rounded_corner_max_radius">28dp</dimen>
 
+    <!-- Width and height used to filter widgets displayed in the communal widget picker -->
+    <dimen name="communal_widget_picker_desired_width">360dp</dimen>
+    <dimen name="communal_widget_picker_desired_height">240dp</dimen>
+
     <!-- The width/height of the unlock icon view on keyguard. -->
     <dimen name="keyguard_lock_height">42dp</dimen>
     <dimen name="keyguard_lock_padding">20dp</dimen>
@@ -940,6 +946,9 @@
     <!-- Padding between signal cluster and battery icon -->
     <dimen name="signal_cluster_battery_padding">6dp</dimen>
 
+    <!-- end padding for battery icon in status bar -->
+    <dimen name="status_bar_battery_end_padding">0dp</dimen>
+
     <!-- Screen pinning request width -->
     <dimen name="screen_pinning_request_width">@dimen/match_parent</dimen>
     <!-- Screen pinning request nav button circle heights -->
@@ -1118,6 +1127,8 @@
     <dimen name="biometric_prompt_panel_max_width">640dp</dimen>
     <dimen name="biometric_prompt_land_small_horizontal_guideline_padding">344dp</dimen>
     <dimen name="biometric_prompt_two_pane_udfps_horizontal_guideline_padding">114dp</dimen>
+    <dimen name="biometric_prompt_two_pane_udfps_shorter_content_width">216dp</dimen>
+    <dimen name="biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding">661dp</dimen>
     <dimen name="biometric_prompt_two_pane_medium_horizontal_guideline_padding">640dp</dimen>
     <dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">119dp</dimen>
     <dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index effaaf2..8381812 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -319,6 +319,35 @@
     <string name="screenrecord_save_error">Error saving screen recording</string>
     <!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] -->
     <string name="screenrecord_start_error">Error starting screen recording</string>
+    <!-- Title for a dialog shown to the user that will let them stop recording their screen [CHAR LIMIT=50] -->
+    <string name="screenrecord_stop_dialog_title">Stop recording screen?</string>
+    <!-- Text telling a user that they will stop recording their screen if they click the "Stop recording" button [CHAR LIMIT=100] -->
+    <string name="screenrecord_stop_dialog_message">You will stop recording your screen</string>
+    <!-- Text telling a user that they will stop recording the contents of the specified [app_name] if they click the "Stop recording" button. Note that the app name will appear in bold. [CHAR LIMIT=100] -->
+    <string name="screenrecord_stop_dialog_message_specific_app">You will stop recording &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
+    <!-- Button to stop a screen recording [CHAR LIMIT=35] -->
+    <string name="screenrecord_stop_dialog_button">Stop recording</string>
+
+    <!-- Title for a dialog shown to the user that will let them stop sharing their screen to another app on the device [CHAR LIMIT=50] -->
+    <string name="share_to_app_stop_dialog_title">Stop sharing screen?</string>
+    <!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] -->
+    <string name="share_to_app_stop_dialog_message">You will stop sharing your screen</string>
+    <!-- Text telling a user that they will stop sharing the contents of the specified [app_name] if they click the "Stop sharing" button. Note that the app name will appear in bold. [CHAR LIMIT=100] -->
+    <string name="share_to_app_stop_dialog_message_specific_app">You will stop sharing &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
+    <!-- Button to stop screen sharing [CHAR LIMIT=35] -->
+    <string name="share_to_app_stop_dialog_button">Stop sharing</string>
+
+    <!-- Title for a dialog shown to the user that will let them stop casting their screen to a different device [CHAR LIMIT=50] -->
+    <string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string>
+    <!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] -->
+    <string name="cast_to_other_device_stop_dialog_message">You will stop casting your screen</string>
+    <!-- Text telling a user that they will stop casting the contents of the specified [app_name] to a different device if they click the "Stop casting" button. Note that the app name will appear in bold.  [CHAR LIMIT=100] -->
+    <string name="cast_to_other_device_stop_dialog_message_specific_app">You will stop casting &lt;b><xliff:g id="app_name" example="Photos App">%1$s</xliff:g>&lt;/b></string>
+    <!-- Button to stop screen casting to a different device [CHAR LIMIT=35] -->
+    <string name="cast_to_other_device_stop_dialog_button">Stop casting</string>
+
+    <!-- Button to close a dialog without doing any action [CHAR LIMIT=20] -->
+    <string name="close_dialog_button">Close</string>
 
     <!-- Notification title displayed for issue recording [CHAR LIMIT=50]-->
     <string name="issuerecord_title">Issue Recorder</string>
@@ -344,7 +373,7 @@
     <!-- Cling help message title when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] -->
     <string name="immersive_cling_title">Viewing full screen</string>
     <!-- Cling help message description when hiding the navigation bar entering immersive mode [CHAR LIMIT=none] -->
-    <string name="immersive_cling_description">To exit, swipe down from the top.</string>
+    <string name="immersive_cling_description">To exit, swipe down from the top of your screen</string>
     <!-- Cling help message confirmation button when hiding the navigation bar entering immersive mode [CHAR LIMIT=30] -->
     <string name="immersive_cling_positive">Got it</string>
 
@@ -693,15 +722,15 @@
     <!-- QuickSettings: Accessibility label to activate a device [CHAR LIMIT=NONE]-->
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate">activate</string>
     <!-- QuickSettings: Bluetooth auto on tomorrow [CHAR LIMIT=NONE]-->
-    <string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on again tomorrow</string>
+    <string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on tomorrow</string>
     <!-- QuickSettings: Bluetooth auto on info text when disabled [CHAR LIMIT=NONE]-->
     <string name="turn_on_bluetooth_auto_info_disabled">Features like Quick Share and Find My Device use Bluetooth</string>
     <!-- QuickSettings: Bluetooth auto on info text when enabled [CHAR LIMIT=NONE]-->
     <string name="turn_on_bluetooth_auto_info_enabled">Bluetooth will turn on tomorrow morning</string>
     <!-- QuickSettings: Bluetooth dialog audio sharing button text [CHAR LIMIT=50]-->
-    <string name="quick_settings_bluetooth_audio_sharing_button">Audio Sharing</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button">Share audio</string>
     <!-- QuickSettings: Bluetooth dialog audio sharing button text when sharing audio [CHAR LIMIT=50]-->
-    <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing Audio</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_sharing">Sharing audio</string>
 
     <!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]-->
     <string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string>
@@ -3094,6 +3123,8 @@
 
     <!-- Accessibility label for a11y action to show the bouncer (pin/pattern/password) screen lock [CHAR LIMIT=NONE] -->
     <string name="accessibility_bouncer">enter screen lock</string>
+    <!-- Accessibility label for side fingerprint sensor indicator [CHAR LIMIT=NONE] -->
+    <string name="accessibility_side_fingerprint_indicator_label">Touch the fingerprint sensor. It\u2019s the shorter button on the side of the phone</string>
     <!-- Accessibility label for fingerprint sensor [CHAR LIMIT=NONE] -->
     <string name="accessibility_fingerprint_label">Fingerprint sensor</string>
     <!-- Accessibility action for tapping on an affordance that will bring up the user's
@@ -3529,6 +3560,23 @@
          use. The helper shows shortcuts in categories, which can be collapsed or expanded.
          [CHAR LIMIT=NONE] -->
     <string name="shortcut_helper_content_description_expand_icon">Expand icon</string>
+    <!-- Word that separates different possible key combinations of a shortcut. For example the
+         "Go to home screen" shortcut could be triggered using "home button" OR "ctrl + h".
+         This is that "or" separator.
+         The keyboard shortcut helper is a  component that shows the  user which keyboard shortcuts
+         they can use.
+         [CHAR LIMIT=NONE]
+          -->
+    <string name="shortcut_helper_key_combinations_or_separator">or</string>
+
+    <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] -->
+    <string name="touchpad_tutorial_back_gesture_button">Back gesture</string>
+    <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] -->
+    <string name="touchpad_tutorial_home_gesture_button">Home gesture</string>
+    <!-- Label for button opening tutorial on using Action key from keyboard [CHAR LIMIT=NONE] -->
+    <string name="touchpad_tutorial_action_key_button">Action key</string>
+    <!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] -->
+    <string name="touchpad_tutorial_done_button">Done</string>
 
     <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
     <string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1e0adec..73b7586 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -338,8 +338,11 @@
         <item name="android:textSize">16sp</item>
     </style>
 
-    <style name="AuthCredentialPanelStyle">
+    <style name="AuthNonCredentialPanelStyle">
         <item name="android:background">?androidprv:attr/materialColorSurfaceBright</item>
+    </style>
+
+    <style name="AuthCredentialPanelStyle" parent="AuthNonCredentialPanelStyle">
         <item name="android:clickable">true</item>
         <item name="android:clipToOutline">true</item>
         <item name="android:importantForAccessibility">no</item>
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
index 8979ef1..12d881b 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -25,7 +25,11 @@
 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
 import android.content.Context
 import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.Canvas
 import android.graphics.Insets
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
 import android.hardware.biometrics.BiometricManager.Authenticators
 import android.hardware.biometrics.PromptInfo
 import android.hardware.biometrics.SensorPropertiesInternal
@@ -122,4 +126,26 @@
         return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars())
             ?: Insets.NONE
     }
+
+    /** Converts `drawable` to a [Bitmap]. */
+    @JvmStatic
+    fun Drawable?.toBitmap(): Bitmap? {
+        if (this == null) {
+            return null
+        }
+        if (this is BitmapDrawable) {
+            return bitmap
+        }
+        val bitmap: Bitmap =
+            if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
+                Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+                // Single color bitmap will be created of 1x1 pixel
+            } else {
+                Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)
+            }
+        val canvas = Canvas(bitmap)
+        setBounds(0, 0, canvas.width, canvas.height)
+        draw(canvas)
+        return bitmap
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
index 7a8c82c..4fd5456 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt
@@ -37,10 +37,17 @@
     private lateinit var rootView: ViewGroup
     private var translationMax = 0f
 
+    /**
+     * Initializes the animator, it is allowed to call this method multiple times, for example
+     * to update the rootView or maximum translation
+     */
     fun init(rootView: ViewGroup, translationMax: Float) {
+        if (!::rootView.isInitialized) {
+            progressProvider.addCallback(this)
+        }
+
         this.rootView = rootView
         this.translationMax = translationMax
-        progressProvider.addCallback(this)
     }
 
     override fun onTransitionStarted() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index c33b7ce..c225cbc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -87,10 +87,15 @@
         mTmpDestinationRect.inset(insets);
         // Scale to the bounds no smaller than the destination and offset such that the top/left
         // of the scaled inset source rect aligns with the top/left of the destination bounds
-        final float scale;
+        final float scale, left, top;
         if (sourceRectHint.isEmpty() || sourceRectHint.width() == sourceBounds.width()) {
             scale = Math.max((float) destinationBounds.width() / sourceBounds.width(),
                     (float) destinationBounds.height() / sourceBounds.height());
+            // Work around the rounding error by fix the position at very beginning.
+            left = scale == 1
+                    ? 0 : destinationBounds.left - (insets.left + sourceBounds.left) * scale;
+            top = scale == 1
+                    ? 0 : destinationBounds.top - (insets.top + sourceBounds.top) * scale;
         } else {
             // scale by sourceRectHint if it's not edge-to-edge
             final float endScale = sourceRectHint.width() <= sourceRectHint.height()
@@ -100,9 +105,9 @@
                     ? (float) destinationBounds.width() / sourceBounds.width()
                     : (float) destinationBounds.height() / sourceBounds.height();
             scale = Math.min((1 - progress) * startScale + progress * endScale, 1.0f);
+            left = destinationBounds.left - (insets.left + sourceBounds.left) * scale;
+            top = destinationBounds.top - (insets.top + sourceBounds.top) * scale;
         }
-        final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale;
-        final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale;
         mTmpTransform.setScale(scale, scale);
         final float cornerRadius = getScaledCornerRadius(mTmpDestinationRect, destinationBounds);
         tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
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 b4377ea..c0c8b75 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
@@ -122,6 +122,8 @@
     public static final long SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY = 1L << 31;
     // Physical keyboard shortcuts helper is showing
     public static final long SYSUI_STATE_SHORTCUT_HELPER_SHOWING = 1L << 32;
+    // Touchpad gestures are disabled
+    public static final long SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED = 1L << 33;
 
     // Mask for SystemUiStateFlags to isolate SYSUI_STATE_AWAKE and
     // SYSUI_STATE_WAKEFULNESS_TRANSITION, to match WAKEFULNESS_* constants
@@ -170,6 +172,7 @@
             SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
             SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY,
             SYSUI_STATE_SHORTCUT_HELPER_SHOWING,
+            SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED,
     })
     public @interface SystemUiStateFlags {}
 
@@ -271,6 +274,9 @@
         if ((flags & SYSUI_STATE_SHORTCUT_HELPER_SHOWING) != 0) {
             str.add("shortcut_helper_showing");
         }
+        if ((flags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) != 0) {
+            str.add("touchpad_gestures_disabled");
+        }
 
         return str.toString();
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 86c807b..5dcf161 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -178,6 +178,7 @@
         smallClockOnAttachStateChangeListener =
             object : OnAttachStateChangeListener {
                 var pastVisibility: Int? = null
+
                 override fun onViewAttachedToWindow(view: View) {
                     clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
                     // Match the asing for view.parent's layout classes.
@@ -213,6 +214,7 @@
                 override fun onViewAttachedToWindow(p0: View) {
                     clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
                 }
+
                 override fun onViewDetachedFromWindow(p0: View) {}
             }
         clock.largeClock.view.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
@@ -284,8 +286,10 @@
 
     var smallRegionSampler: RegionSampler? = null
         private set
+
     var largeRegionSampler: RegionSampler? = null
         private set
+
     var smallTimeListener: TimeListener? = null
     var largeTimeListener: TimeListener? = null
     val shouldTimeListenerRun: Boolean
@@ -560,7 +564,7 @@
     internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
         return scope.launch {
             keyguardTransitionInteractor
-                .transitionStepsToState(AOD)
+                .transition(Edge.create(to = AOD))
                 .filter { it.transitionState == TransitionState.STARTED }
                 .filter { it.from != LOCKSCREEN }
                 .collect { handleDoze(1f) }
@@ -571,7 +575,7 @@
     internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
         return scope.launch {
             keyguardTransitionInteractor
-                .transitionStepsToState(LOCKSCREEN)
+                .transition(Edge.create(to = LOCKSCREEN))
                 .filter { it.transitionState == TransitionState.STARTED }
                 .filter { it.from != AOD }
                 .collect { handleDoze(0f) }
@@ -586,7 +590,7 @@
     internal fun listenForAnyStateToDozingTransition(scope: CoroutineScope): Job {
         return scope.launch {
             keyguardTransitionInteractor
-                .transitionStepsToState(DOZING)
+                .transition(Edge.create(to = DOZING))
                 .filter { it.transitionState == TransitionState.FINISHED }
                 .collect { handleDoze(1f) }
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 7170be61..19d918f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -17,16 +17,21 @@
 package com.android.keyguard
 
 import android.content.Context
-import android.view.ViewGroup
-import com.android.systemui.res.R
+import android.view.View
+import com.android.systemui.keyguard.MigrateClocksToBlueprint
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.StatusBarState.KEYGUARD
+import com.android.systemui.res.R
+import com.android.systemui.shared.R as sharedR
+import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START
 import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
+import com.android.systemui.statusbar.StatusBarState.KEYGUARD
 import com.android.systemui.unfold.SysUIUnfoldScope
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.dagger.NaturalRotation
 import javax.inject.Inject
 
 /**
@@ -38,8 +43,10 @@
 @Inject
 constructor(
     private val context: Context,
+    private val keyguardRootView: KeyguardRootView,
+    private val shadeWindowView: NotificationShadeWindowView,
     statusBarStateController: StatusBarStateController,
-    unfoldProgressProvider: NaturalRotationUnfoldProgressProvider,
+    @NaturalRotation unfoldProgressProvider: UnfoldTransitionProgressProvider,
 ) {
 
     /** Certain views only need to move if they are not currently centered */
@@ -50,27 +57,94 @@
     private val filterKeyguard: () -> Boolean = { statusBarStateController.getState() == KEYGUARD }
 
     private val translateAnimator by lazy {
+        val smartSpaceViews = if (MigrateClocksToBlueprint.isEnabled) {
+            // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer]
+            val scrollXTranslation = { view: View, translation: Float ->
+                view.scrollX = -translation.toInt()
+            }
+
+            setOf(
+                ViewIdToTranslate(
+                    viewId = sharedR.id.date_smartspace_view,
+                    direction = START,
+                    shouldBeAnimated = filterKeyguard,
+                    translateFunc = scrollXTranslation,
+                ),
+                ViewIdToTranslate(
+                    viewId = sharedR.id.bc_smartspace_view,
+                    direction = START,
+                    shouldBeAnimated = filterKeyguard,
+                    translateFunc = scrollXTranslation,
+                ),
+                ViewIdToTranslate(
+                    viewId = sharedR.id.weather_smartspace_view,
+                    direction = START,
+                    shouldBeAnimated = filterKeyguard,
+                    translateFunc = scrollXTranslation,
+                )
+            )
+        } else {
+            setOf(ViewIdToTranslate(
+                viewId = R.id.keyguard_status_area,
+                direction = START,
+                shouldBeAnimated = filterKeyguard,
+                translateFunc = { view, value ->
+                    (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value
+                }
+            ))
+        }
+
         UnfoldConstantTranslateAnimator(
             viewsIdToTranslate =
                 setOf(
-                    ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard,
-                        { view, value ->
-                            (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value
-                        }),
                     ViewIdToTranslate(
-                        R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly),
-                    ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard),
+                        viewId = R.id.lockscreen_clock_view_large,
+                        direction = START,
+                        shouldBeAnimated = filterKeyguardAndSplitShadeOnly
+                    ),
                     ViewIdToTranslate(
-                        R.id.notification_stack_scroller, END, filterKeyguardAndSplitShadeOnly),
-                    ViewIdToTranslate(R.id.start_button, START, filterKeyguard),
-                    ViewIdToTranslate(R.id.end_button, END, filterKeyguard)),
-            progressProvider = unfoldProgressProvider)
+                        viewId = R.id.lockscreen_clock_view,
+                        direction = START,
+                        shouldBeAnimated = filterKeyguard
+                    ),
+                    ViewIdToTranslate(
+                        viewId = R.id.notification_stack_scroller,
+                        direction = END,
+                        shouldBeAnimated = filterKeyguardAndSplitShadeOnly
+                    )
+                ) + smartSpaceViews,
+            progressProvider = unfoldProgressProvider
+        )
     }
 
-    /** Relies on the [parent] to locate views to translate. */
-    fun setup(parent: ViewGroup) {
+    private val shortcutButtonsAnimator by lazy {
+        UnfoldConstantTranslateAnimator(
+            viewsIdToTranslate =
+            setOf(
+                ViewIdToTranslate(
+                    viewId = R.id.start_button,
+                    direction = START,
+                    shouldBeAnimated = filterKeyguard
+                ),
+                ViewIdToTranslate(
+                    viewId = R.id.end_button,
+                    direction = END,
+                    shouldBeAnimated = filterKeyguard
+                )
+            ),
+            progressProvider = unfoldProgressProvider
+        )
+    }
+
+    /** Initializes the keyguard fold/unfold transition */
+    fun setup() {
         val translationMax =
             context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
-        translateAnimator.init(parent, translationMax)
+
+        translateAnimator.init(shadeWindowView, translationMax)
+
+        // Use keyguard root view as there is another instance of start/end buttons with the same ID
+        // outside of the keyguard root view
+        shortcutButtonsAnimator.init(keyguardRootView, translationMax)
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
index a9fd340..03b13fe 100644
--- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
@@ -70,7 +70,7 @@
 import com.android.systemui.keyguard.MigrateClocksToBlueprint;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
@@ -167,9 +167,9 @@
     private LockIconView mView;
 
     @VisibleForTesting
-    final Consumer<TransitionStep> mDozeTransitionCallback = (TransitionStep step) -> {
-        mInterpolatedDarkAmount = step.getValue();
-        mView.setDozeAmount(step.getValue());
+    final Consumer<Float> mDozeTransitionCallback = (Float value) -> {
+        mInterpolatedDarkAmount = value;
+        mView.setDozeAmount(value);
         updateBurnInOffsets();
     };
 
@@ -265,7 +265,7 @@
         mView.setAccessibilityDelegate(mAccessibilityDelegate);
 
         if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
-            collectFlow(mView, mTransitionInteractor.getDozeAmountTransition(),
+            collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD),
                     mDozeTransitionCallback);
             collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 4c9af66..e055e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -1250,6 +1250,11 @@
         if (mOverlays == null) {
             return;
         }
+        if (mPendingConfigChange) {
+            // Let RestartingPreDrawListener's onPreDraw call updateConfiguration
+            // -> updateOverlayProviderViews to redraw with display change synchronously.
+            return;
+        }
         ++mProviderRefreshToken;
         for (final OverlayWindow overlay: mOverlays) {
             if (overlay == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 0bd6d6e..3c4c003 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -30,7 +30,12 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Handler;
+import android.util.Log;
 import android.view.AttachedSurfaceControl;
+import android.view.Display;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
 import android.view.LayoutInflater;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
@@ -46,15 +51,18 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.res.R;
+import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 
 class FullscreenMagnificationController implements ComponentCallbacks {
 
+    private static final String TAG = "FullscreenMagnificationController";
     private final Context mContext;
     private final AccessibilityManager mAccessibilityManager;
     private final WindowManager mWindowManager;
+    private final IWindowManager mIWindowManager;
     private Supplier<SurfaceControlViewHost> mScvhSupplier;
     private SurfaceControlViewHost mSurfaceControlViewHost = null;
     private SurfaceControl mBorderSurfaceControl = null;
@@ -65,33 +73,50 @@
     private final int mDisplayId;
     private static final Region sEmptyRegion = new Region();
     private ValueAnimator mShowHideBorderAnimator;
+    private Handler mHandler;
     private Executor mExecutor;
     private boolean mFullscreenMagnificationActivated = false;
     private final Configuration mConfiguration;
+    private final Runnable mShowBorderRunnable = this::showBorderWithNullCheck;
+    private int mRotation;
+    private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() {
+        @Override
+        public void onRotationChanged(final int rotation) {
+            handleScreenRotation();
+        }
+    };
+    private final long mLongAnimationTimeMs;
 
     FullscreenMagnificationController(
             @UiContext Context context,
-            Executor executor,
+            @Main Handler handler,
+            @Main Executor executor,
             AccessibilityManager accessibilityManager,
             WindowManager windowManager,
+            IWindowManager iWindowManager,
             Supplier<SurfaceControlViewHost> scvhSupplier) {
-        this(context, executor, accessibilityManager, windowManager, scvhSupplier,
-                new SurfaceControl.Transaction(), createNullTargetObjectAnimator(context));
+        this(context, handler, executor, accessibilityManager,
+                windowManager, iWindowManager, scvhSupplier,
+                new SurfaceControl.Transaction(), null);
     }
 
     @VisibleForTesting
     FullscreenMagnificationController(
             @UiContext Context context,
+            @Main Handler handler,
             @Main Executor executor,
             AccessibilityManager accessibilityManager,
             WindowManager windowManager,
+            IWindowManager iWindowManager,
             Supplier<SurfaceControlViewHost> scvhSupplier,
             SurfaceControl.Transaction transaction,
             ValueAnimator valueAnimator) {
         mContext = context;
+        mHandler = handler;
         mExecutor = executor;
         mAccessibilityManager = accessibilityManager;
         mWindowManager = windowManager;
+        mIWindowManager = iWindowManager;
         mWindowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
         mTransaction = transaction;
         mScvhSupplier = scvhSupplier;
@@ -101,7 +126,10 @@
                 R.dimen.magnifier_border_width_fullscreen);
         mDisplayId = mContext.getDisplayId();
         mConfiguration = new Configuration(context.getResources().getConfiguration());
-        mShowHideBorderAnimator = valueAnimator;
+        mLongAnimationTimeMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_longAnimTime);
+        mShowHideBorderAnimator = (valueAnimator == null)
+                ? createNullTargetObjectAnimator() : valueAnimator;
         mShowHideBorderAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
@@ -114,15 +142,13 @@
         });
     }
 
-    private static ValueAnimator createNullTargetObjectAnimator(Context context) {
+    private ValueAnimator createNullTargetObjectAnimator() {
         final ValueAnimator valueAnimator =
                 ObjectAnimator.ofFloat(/* target= */ null, View.ALPHA, 0f, 1f);
         Interpolator interpolator = new AccelerateDecelerateInterpolator();
-        final long longAnimationDuration = context.getResources().getInteger(
-                com.android.internal.R.integer.config_longAnimTime);
 
         valueAnimator.setInterpolator(interpolator);
-        valueAnimator.setDuration(longAnimationDuration);
+        valueAnimator.setDuration(mLongAnimationTimeMs);
         return valueAnimator;
     }
 
@@ -149,7 +175,11 @@
      */
     @UiThread
     private void removeFullscreenMagnificationBorder() {
+        if (mHandler.hasCallbacks(mShowBorderRunnable)) {
+            mHandler.removeCallbacks(mShowBorderRunnable);
+        }
         mContext.unregisterComponentCallbacks(this);
+
         mShowHideBorderAnimator.reverse();
     }
 
@@ -161,6 +191,11 @@
 
         if (mFullscreenBorder != null) {
             mFullscreenBorder = null;
+            try {
+                mIWindowManager.removeRotationWatcher(mRotationWatcher);
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to remove rotation watcher", e);
+            }
         }
     }
 
@@ -186,6 +221,11 @@
             mSurfaceControlViewHost = mScvhSupplier.get();
             mSurfaceControlViewHost.setView(mFullscreenBorder, getBorderLayoutParams());
             mBorderSurfaceControl = mSurfaceControlViewHost.getSurfacePackage().getSurfaceControl();
+            try {
+                mIWindowManager.watchRotation(mRotationWatcher, Display.DEFAULT_DISPLAY);
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to register rotation watcher", e);
+            }
         }
 
         mTransaction
@@ -256,11 +296,55 @@
             reCreateWindow = true;
         }
 
-        if (mFullscreenBorder != null && reCreateWindow) {
+        if (mFullscreenBorder == null) {
+            return;
+        }
+
+        if (reCreateWindow) {
             final int newWidth = mWindowBounds.width() + 2 * mBorderOffset;
             final int newHeight = mWindowBounds.height() + 2 * mBorderOffset;
             mSurfaceControlViewHost.relayout(newWidth, newHeight);
         }
+
+        // Rotating from Landscape to ReverseLandscape will not trigger the config changes in
+        // CONFIG_SCREEN_SIZE and CONFIG_ORIENTATION. Therefore, we would like to check the device
+        // rotation separately.
+        // Since there's a possibility that {@link onConfigurationChanged} comes before
+        // {@link onRotationChanged}, we would like to handle screen rotation in either case that
+        // happens earlier.
+        int newRotation = RotationUtils.getRotation(mContext);
+        if (newRotation != mRotation) {
+            mRotation = newRotation;
+            handleScreenRotation();
+        }
+    }
+
+    private boolean isActivated() {
+        return mFullscreenBorder != null;
+    }
+
+    private void handleScreenRotation() {
+        if (!isActivated()) {
+            return;
+        }
+
+        if (mHandler.hasCallbacks(mShowBorderRunnable)) {
+            mHandler.removeCallbacks(mShowBorderRunnable);
+        }
+
+        // We hide the border immediately as early as possible to beat the redrawing of window
+        // in response to the orientation change so users won't see a weird shape border.
+        mHandler.postAtFrontOfQueue(() -> {
+            mFullscreenBorder.setAlpha(0f);
+        });
+
+        mHandler.postDelayed(mShowBorderRunnable, mLongAnimationTimeMs);
+    }
+
+    private void showBorderWithNullCheck() {
+        if (mShowHideBorderAnimator != null) {
+            mShowHideBorderAnimator.start();
+        }
     }
 
     private void updateDimensions() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
index 35c2024..e22a4e4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/Magnification.java
@@ -34,6 +34,7 @@
 import android.os.Message;
 import android.util.SparseArray;
 import android.view.Display;
+import android.view.IWindowManager;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.WindowManager;
@@ -148,13 +149,19 @@
             DisplayIdIndexSupplier<FullscreenMagnificationController> {
 
         private final Context mContext;
+        private final Handler mHandler;
         private final Executor mExecutor;
+        private final IWindowManager mIWindowManager;
 
-        FullscreenMagnificationControllerSupplier(Context context, DisplayManager displayManager,
-                Executor executor) {
+        FullscreenMagnificationControllerSupplier(Context context,
+                DisplayManager displayManager,
+                Handler handler,
+                Executor executor, IWindowManager iWindowManager) {
             super(displayManager);
             mContext = context;
+            mHandler = handler;
             mExecutor = executor;
+            mIWindowManager = iWindowManager;
         }
 
         @Override
@@ -166,9 +173,11 @@
             windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
             return new FullscreenMagnificationController(
                     windowContext,
+                    mHandler,
                     mExecutor,
                     windowContext.getSystemService(AccessibilityManager.class),
                     windowContext.getSystemService(WindowManager.class),
+                    mIWindowManager,
                     scvhSupplier);
         }
     }
@@ -211,14 +220,16 @@
     DisplayIdIndexSupplier<MagnificationSettingsController> mMagnificationSettingsSupplier;
 
     @Inject
-    public Magnification(Context context, @Main Handler mainHandler, @Main Executor executor,
+    public Magnification(Context context,
+            @Main Handler mainHandler, @Main Executor executor,
             CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
             SysUiState sysUiState, OverviewProxyService overviewProxyService,
             SecureSettings secureSettings, DisplayTracker displayTracker,
-            DisplayManager displayManager, AccessibilityLogger a11yLogger) {
+            DisplayManager displayManager, AccessibilityLogger a11yLogger,
+            IWindowManager iWindowManager) {
         this(context, mainHandler.getLooper(), executor, commandQueue,
                 modeSwitchesController, sysUiState, overviewProxyService, secureSettings,
-                displayTracker, displayManager, a11yLogger);
+                displayTracker, displayManager, a11yLogger, iWindowManager);
     }
 
     @VisibleForTesting
@@ -226,7 +237,8 @@
             CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
             SysUiState sysUiState, OverviewProxyService overviewProxyService,
             SecureSettings secureSettings, DisplayTracker displayTracker,
-            DisplayManager displayManager, AccessibilityLogger a11yLogger) {
+            DisplayManager displayManager, AccessibilityLogger a11yLogger,
+            IWindowManager iWindowManager) {
         mContext = context;
         mHandler = new Handler(looper) {
             @Override
@@ -248,7 +260,7 @@
                 mHandler, mWindowMagnifierCallback,
                 displayManager, sysUiState, secureSettings);
         mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
-                context, displayManager, mExecutor);
+                context, displayManager, mHandler, mExecutor, iWindowManager);
         mMagnificationSettingsSupplier = new SettingsSupplier(context,
                 mMagnificationSettingsControllerCallback, displayManager, secureSettings);
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index dafd5f8..030d147 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -72,6 +72,7 @@
     private final Handler mHandler;
     private boolean mIsFadeEffectEnabled;
     private Runnable mSpringAnimationsEndAction;
+    private PointF mAnimationEndPosition = new PointF();
 
     // Cache the animations state of {@link DynamicAnimation.TRANSLATION_X} and {@link
     // DynamicAnimation.TRANSLATION_Y} to be well controlled by the touch handler
@@ -104,10 +105,12 @@
                     @Override
                     public void onRadiiAnimationStop() {}
                 });
+        mAnimationEndPosition = mMenuView.getMenuPosition();
     }
 
     void moveToPosition(PointF position) {
         moveToPosition(position, /* animateMovement = */ false);
+        mAnimationEndPosition = position;
     }
 
     /* Moves position without updating underlying percentage position. Can be animated. */
@@ -129,6 +132,7 @@
         } else {
             DynamicAnimation.TRANSLATION_X.setValue(mMenuView, positionX);
         }
+        mAnimationEndPosition.x = positionX;
     }
 
     void moveToPositionY(float positionY) {
@@ -144,6 +148,7 @@
         } else {
             DynamicAnimation.TRANSLATION_Y.setValue(mMenuView, positionY);
         }
+        mAnimationEndPosition.y = positionY;
     }
 
     void moveToPositionYIfNeeded(float positionY) {
@@ -259,6 +264,9 @@
 
         cancelAnimation(property);
         mPositionAnimations.put(property, flingAnimation);
+        if (finalPosition != null) {
+            setAnimationEndPosition(property, finalPosition);
+        }
         flingAnimation.start();
     }
 
@@ -292,6 +300,7 @@
 
         cancelAnimation(property);
         mPositionAnimations.put(property, springAnimation);
+        setAnimationEndPosition(property, finalPosition);
         springAnimation.animateToFinalPosition(finalPosition);
     }
 
@@ -385,6 +394,21 @@
         mPositionAnimations.get(property).cancel();
     }
 
+    private void setAnimationEndPosition(
+            DynamicAnimation.ViewProperty property, Float endPosition) {
+        if (property.equals(DynamicAnimation.TRANSLATION_X)) {
+            mAnimationEndPosition.x = endPosition;
+        }
+        if (property.equals(DynamicAnimation.TRANSLATION_Y)) {
+            mAnimationEndPosition.y = endPosition;
+        }
+    }
+
+    void skipAnimations() {
+        cancelAnimations();
+        moveToPosition(mAnimationEndPosition, false);
+    }
+
     @VisibleForTesting
     DynamicAnimation getAnimation(DynamicAnimation.ViewProperty property) {
         return mPositionAnimations.getOrDefault(property, null);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 0c67c50..ae9775a0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -334,6 +334,7 @@
         mDragToInteractView.updateResources();
         mDismissView.updateResources();
         mDragToInteractAnimationController.updateResources();
+        mMenuAnimationController.skipAnimations();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java
new file mode 100644
index 0000000..2d1cd03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesChecker.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.hearingaid;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.dagger.SysUISingleton;
+
+import javax.inject.Inject;
+
+/**
+ * HearingDevicesChecker provides utility methods to determine the presence and status of
+ * connected hearing aid devices.
+ *
+ * <p>It also filters out devices that are exclusively managed by other applications to avoid
+ * interfering with their operation.
+ */
+@SysUISingleton
+public class HearingDevicesChecker {
+
+    private final Context mContext;
+    private final LocalBluetoothManager mLocalBluetoothManager;
+
+    @Inject
+    public HearingDevicesChecker(
+            Context context,
+            @Nullable LocalBluetoothManager localBluetoothManager) {
+        mContext = context;
+        mLocalBluetoothManager = localBluetoothManager;
+    }
+
+    /**
+     * Checks if any hearing device is already paired.
+     *
+     * <p>It includes {@link BluetoothDevice.BOND_BONDING} and {@link BluetoothDevice.BOND_BONDED}).
+     *
+     * <p>A bonded device means it has been paired, but may not connected now.
+     *
+     * @return {@code true} if any bonded hearing device is found, {@code false} otherwise.
+     */
+    @WorkerThread
+    public boolean isAnyPairedHearingDevice() {
+        if (mLocalBluetoothManager == null) {
+            return false;
+        }
+        if (!mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) {
+            return false;
+        }
+
+        return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream()
+                .anyMatch(device -> device.isHearingAidDevice()
+                        && device.getBondState() != BluetoothDevice.BOND_NONE
+                        && !isExclusivelyManagedBluetoothDevice(device));
+    }
+
+    /**
+     * Checks if there are any active hearing device.
+     *
+     * <p>An active device means it is currently connected and streaming media.
+     *
+     * @return {@code true} if any active hearing device is found, {@code false} otherwise.
+     */
+    @WorkerThread
+    public boolean isAnyActiveHearingDevice() {
+        if (mLocalBluetoothManager == null) {
+            return false;
+        }
+        if (!mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) {
+            return false;
+        }
+
+        return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream()
+                .anyMatch(device -> BluetoothUtils.isActiveMediaDevice(device)
+                        && BluetoothUtils.isAvailableHearingDevice(device)
+                        && !isExclusivelyManagedBluetoothDevice(device));
+    }
+
+    private boolean isExclusivelyManagedBluetoothDevice(
+            @NonNull CachedBluetoothDevice cachedDevice) {
+        if (com.android.settingslib.flags.Flags.enableHideExclusivelyManagedBluetoothDevice()) {
+            return BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
+                    cachedDevice.getDevice());
+        }
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
index 14e5f34..bc4cb45 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
@@ -16,19 +16,24 @@
 
 package com.android.systemui.accessibility.hearingaid;
 
-import android.bluetooth.BluetoothDevice;
 import android.util.Log;
 
-import androidx.annotation.Nullable;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
 
 import com.android.internal.jank.InteractionJankMonitor;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.animation.DialogCuj;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 
 /**
@@ -43,16 +48,22 @@
     private SystemUIDialog mDialog;
     private final DialogTransitionAnimator mDialogTransitionAnimator;
     private final HearingDevicesDialogDelegate.Factory mDialogFactory;
-    private final LocalBluetoothManager mLocalBluetoothManager;
+    private final HearingDevicesChecker mDevicesChecker;
+    private final Executor mBackgroundExecutor;
+    private final Executor mMainExecutor;
 
     @Inject
     public HearingDevicesDialogManager(
             DialogTransitionAnimator dialogTransitionAnimator,
             HearingDevicesDialogDelegate.Factory dialogFactory,
-            @Nullable LocalBluetoothManager localBluetoothManager) {
+            HearingDevicesChecker devicesChecker,
+            @Background Executor backgroundExecutor,
+            @Main Executor mainExecutor) {
         mDialogTransitionAnimator = dialogTransitionAnimator;
         mDialogFactory = dialogFactory;
-        mLocalBluetoothManager = localBluetoothManager;
+        mDevicesChecker = devicesChecker;
+        mBackgroundExecutor = backgroundExecutor;
+        mMainExecutor = mainExecutor;
     }
 
     /**
@@ -68,36 +79,41 @@
             destroyDialog();
         }
 
-        mDialog = mDialogFactory.create(!isAnyBondedHearingDevice()).createDialog();
+        final ListenableFuture<Boolean> pairedHearingDeviceCheckTask =
+                CallbackToFutureAdapter.getFuture(completer -> {
+                    mBackgroundExecutor.execute(
+                            () -> {
+                                completer.set(mDevicesChecker.isAnyPairedHearingDevice());
+                            });
+                    // This value is used only for debug purposes: it will be used in toString()
+                    // of returned future or error cases.
+                    return "isAnyPairedHearingDevice check";
+                });
+        pairedHearingDeviceCheckTask.addListener(() -> {
+            try {
+                mDialog = mDialogFactory.create(!pairedHearingDeviceCheckTask.get()).createDialog();
 
-        if (expandable != null) {
-            DialogTransitionAnimator.Controller controller = expandable.dialogTransitionController(
-                    new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                            INTERACTION_JANK_TAG));
-            if (controller != null) {
-                mDialogTransitionAnimator.show(mDialog,
-                        controller, /* animateBackgroundBoundsChange= */ true);
-                return;
+                if (expandable != null) {
+                    DialogTransitionAnimator.Controller controller =
+                            expandable.dialogTransitionController(
+                                    new DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                            INTERACTION_JANK_TAG));
+                    if (controller != null) {
+                        mDialogTransitionAnimator.show(mDialog,
+                                controller, /* animateBackgroundBoundsChange= */ true);
+                        return;
+                    }
+                }
+                mDialog.show();
+
+            } catch (InterruptedException | ExecutionException e) {
+                Log.e(TAG, "Exception occurs while running pairedHearingDeviceCheckTask", e);
             }
-        }
-        mDialog.show();
+        }, mMainExecutor);
     }
 
     private void destroyDialog() {
         mDialog.dismiss();
         mDialog = null;
     }
-
-    private boolean isAnyBondedHearingDevice() {
-        if (mLocalBluetoothManager == null) {
-            return false;
-        }
-        if (!mLocalBluetoothManager.getBluetoothAdapter().isEnabled()) {
-            return false;
-        }
-
-        return mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy().stream()
-                .anyMatch(device -> device.isHearingAidDevice()
-                        && device.getBondState() != BluetoothDevice.BOND_NONE);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index ed9597d..03f282e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.qs.tiles.NightDisplayTile
 import com.android.systemui.qs.tiles.OneHandedModeTile
 import com.android.systemui.qs.tiles.ReduceBrightColorsTile
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.ColorCorrectionTileMapper
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor
@@ -115,6 +116,48 @@
     @StringKey(HearingDevicesTile.TILE_SPEC)
     fun bindHearingDevicesTile(hearingDevicesTile: HearingDevicesTile): QSTileImpl<*>
 
+    @Binds
+    @IntoMap
+    @StringKey(COLOR_CORRECTION_TILE_SPEC)
+    fun provideColorCorrectionAvailabilityInteractor(
+            impl: ColorCorrectionTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(COLOR_INVERSION_TILE_SPEC)
+    fun provideColorInversionAvailabilityInteractor(
+            impl: ColorCorrectionTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(FONT_SCALING_TILE_SPEC)
+    fun provideFontScalingAvailabilityInteractor(
+            impl: FontScalingTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(REDUCE_BRIGHTNESS_TILE_SPEC)
+    fun provideReduceBrightnessAvailabilityInteractor(
+            impl: ReduceBrightColorsTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(ONE_HANDED_TILE_SPEC)
+    fun provideOneHandedAvailabilityInteractor(
+            impl: OneHandedModeTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(NIGHT_DISPLAY_TILE_SPEC)
+    fun provideNightDisplayAvailabilityInteractor(
+            impl: NightDisplayTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
     companion object {
         const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
         const val COLOR_INVERSION_TILE_SPEC = "inversion"
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
index ea00398..b0314d8 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
@@ -16,11 +16,19 @@
 
 package com.android.systemui.ambient.dagger
 
+import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
 import com.android.systemui.ambient.touch.dagger.InputSessionComponent
 import dagger.Module
 
-@Module(subcomponents = [AmbientTouchComponent::class, InputSessionComponent::class])
+@Module(
+    subcomponents =
+        [
+            AmbientStatusBarComponent::class,
+            AmbientTouchComponent::class,
+            InputSessionComponent::class,
+        ]
+)
 interface AmbientModule {
     companion object {
         const val TOUCH_HANDLERS = "touch_handlers"
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt
new file mode 100644
index 0000000..8ad4d00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/dagger/AmbientStatusBarComponent.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.ambient.statusbar.dagger
+
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
+import dagger.BindsInstance
+import dagger.Subcomponent
+
+/**
+ * [AmbientStatusBarComponent] can be used for displaying a status bar over ambient surfaces like
+ * the dream or communal hub.
+ */
+@Subcomponent
+interface AmbientStatusBarComponent {
+    @Subcomponent.Factory
+    interface Factory {
+        fun create(
+            @BindsInstance view: AmbientStatusBarView,
+        ): AmbientStatusBarComponent
+    }
+
+    /** Builds a [AmbientStatusBarViewController] */
+    fun getController(): AmbientStatusBarViewController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
rename to packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
index 8e77079..aa96231 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
@@ -39,10 +39,10 @@
 import java.util.Objects;
 
 /**
- * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
+ * {@link AmbientStatusBarView} is the view responsible for displaying the status bar in a
  * dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
  */
-public class DreamOverlayStatusBarView extends ConstraintLayout {
+public class AmbientStatusBarView extends ConstraintLayout {
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = { "STATUS_ICON_" }, value = {
@@ -76,20 +76,20 @@
     private static final float KEY_SHADOW_ALPHA = 0.35f;
     private static final float AMBIENT_SHADOW_ALPHA = 0.4f;
 
-    public DreamOverlayStatusBarView(Context context) {
+    public AmbientStatusBarView(Context context) {
         this(context, null);
     }
 
-    public DreamOverlayStatusBarView(Context context, AttributeSet attrs) {
+    public AmbientStatusBarView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public AmbientStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
         mContext = context;
     }
 
-    public DreamOverlayStatusBarView(
+    public AmbientStatusBarView(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
similarity index 83%
rename from packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
rename to packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
index da72a56..abdc333 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.dreams;
+package com.android.systemui.ambient.statusbar.ui;
 
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
@@ -30,21 +30,24 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamLogger;
+import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider;
 import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
-import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.dagger.DreamLog;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository;
+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.policy.IndividualSensorPrivacyController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
-import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.time.DateFormatUtil;
 
@@ -59,13 +62,11 @@
 import javax.inject.Inject;
 
 /**
- * View controller for {@link DreamOverlayStatusBarView}.
+ * View controller for {@link AmbientStatusBarView}.
  */
-@DreamOverlayComponent.DreamOverlayScope
-public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
+public class AmbientStatusBarViewController extends ViewController<AmbientStatusBarView> {
     private static final String TAG = "DreamStatusBarCtrl";
 
-    private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
     private final NextAlarmController mNextAlarmController;
     private final AlarmManager mAlarmManager;
     private final Resources mResources;
@@ -76,15 +77,17 @@
     private final ZenModeController mZenModeController;
     private final DreamOverlayStateController mDreamOverlayStateController;
     private final UserTracker mUserTracker;
-    private final WifiRepository mWifiRepository;
+    private final WifiInteractor mWifiInteractor;
     private final StatusBarWindowStateController mStatusBarWindowStateController;
     private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider;
     private final Executor mMainExecutor;
     private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems =
             new ArrayList<>();
+    private final CommunalSceneInteractor mCommunalSceneInteractor;
     private final DreamLogger mLogger;
 
     private boolean mIsAttached;
+    private boolean mCommunalVisible;
 
     // Whether dream entry animations are finished.
     private boolean mEntryAnimationsFinished = false;
@@ -115,7 +118,7 @@
 
     private final DreamOverlayNotificationCountProvider.Callback mNotificationCountCallback =
             notificationCount -> showIcon(
-                    DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS,
+                    AmbientStatusBarView.STATUS_ICON_NOTIFICATIONS,
                     notificationCount > 0,
                     notificationCount > 0
                             ? buildNotificationsContentDescription(notificationCount)
@@ -125,11 +128,10 @@
             this::onStatusBarItemsChanged;
 
     @Inject
-    public DreamOverlayStatusBarViewController(
-            DreamOverlayStatusBarView view,
+    public AmbientStatusBarViewController(
+            AmbientStatusBarView view,
             @Main Resources resources,
             @Main Executor mainExecutor,
-            TouchInsetManager.TouchInsetSession touchInsetSession,
             AlarmManager alarmManager,
             NextAlarmController nextAlarmController,
             DateFormatUtil dateFormatUtil,
@@ -140,12 +142,12 @@
             DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
             DreamOverlayStateController dreamOverlayStateController,
             UserTracker userTracker,
-            WifiRepository wifiRepository,
+            WifiInteractor wifiInteractor,
+            CommunalSceneInteractor communalSceneInteractor,
             @DreamLog LogBuffer logBuffer) {
         super(view);
         mResources = resources;
         mMainExecutor = mainExecutor;
-        mTouchInsetSession = touchInsetSession;
         mAlarmManager = alarmManager;
         mNextAlarmController = nextAlarmController;
         mDateFormatUtil = dateFormatUtil;
@@ -156,7 +158,8 @@
         mZenModeController = zenModeController;
         mDreamOverlayStateController = dreamOverlayStateController;
         mUserTracker = userTracker;
-        mWifiRepository = wifiRepository;
+        mWifiInteractor = wifiInteractor;
+        mCommunalSceneInteractor = communalSceneInteractor;
         mLogger = new DreamLogger(logBuffer, TAG);
 
         // Register to receive show/hide updates for the system status bar. Our custom status bar
@@ -170,10 +173,16 @@
 
         collectFlow(
                 mView,
-                mWifiRepository.getWifiNetwork(),
+                mWifiInteractor.getWifiNetwork(),
                 network -> updateWifiUnavailableStatusIcon(
                         network instanceof WifiNetworkModel.Active));
 
+        collectFlow(
+                mView,
+                mCommunalSceneInteractor.isCommunalVisible(),
+                this::onCommunalVisibleChanged
+        );
+
         mNextAlarmController.addCallback(mNextAlarmCallback);
         updateAlarmStatusIcon();
 
@@ -202,7 +211,6 @@
         mView.removeAllExtraStatusBarItemViews();
         mDreamOverlayStateController.setDreamOverlayStatusBarVisible(false);
         mDreamOverlayStateController.removeCallback(mDreamOverlayStateCallback);
-        mTouchInsetSession.clear();
 
         mIsAttached = false;
     }
@@ -212,7 +220,7 @@
      *
      * No-op if the dream overlay status bar should not be shown.
      */
-    protected void setFadeAmount(float fadeAmount, boolean fadingOut) {
+    public void setFadeAmount(float fadeAmount, boolean fadingOut) {
         updateVisibility();
 
         if (mView.getVisibility() != View.VISIBLE) {
@@ -233,14 +241,20 @@
         mView.setTranslationY(translationY);
     }
 
+    private void onCommunalVisibleChanged(boolean visible) {
+        mCommunalVisible = visible;
+        updateVisibility();
+    }
+
     private boolean shouldShowStatusBar() {
-        return !mDreamOverlayStateController.isLowLightActive()
-                && !mStatusBarWindowStateController.windowIsShowing();
+        return (!mDreamOverlayStateController.isLowLightActive()
+                && !mStatusBarWindowStateController.windowIsShowing())
+                || mCommunalVisible;
     }
 
     @VisibleForTesting
     void updateWifiUnavailableStatusIcon(boolean available) {
-        showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
+        showIcon(AmbientStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available,
                 R.string.wifi_unavailable_dream_overlay_content_description);
     }
 
@@ -249,13 +263,13 @@
                 mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
         final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET,
+                AmbientStatusBarView.STATUS_ICON_ALARM_SET,
                 hasAlarm,
                 hasAlarm ? buildAlarmContentDescription(alarm) : null);
     }
 
     private void updateAssistantAttentionIcon() {
-        showIcon(DreamOverlayStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
+        showIcon(AmbientStatusBarView.STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE,
                 mDreamOverlayStateController.hasAssistantAttention(),
                 R.string.assistant_attention_content_description);
     }
@@ -284,17 +298,17 @@
                 .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE);
         final boolean cameraBlocked = mSensorPrivacyController
                 .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
-        @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
+        @AmbientStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
+                AmbientStatusBarView.STATUS_ICON_CAMERA_DISABLED,
                 !micBlocked && cameraBlocked,
                 R.string.camera_blocked_dream_overlay_content_description);
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
+                AmbientStatusBarView.STATUS_ICON_MIC_DISABLED,
                 micBlocked && !cameraBlocked,
                 R.string.microphone_blocked_dream_overlay_content_description);
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+                AmbientStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
                 micBlocked && cameraBlocked,
                 R.string.camera_and_microphone_blocked_dream_overlay_content_description);
     }
@@ -308,24 +322,24 @@
 
     private void updatePriorityModeStatusIcon() {
         showIcon(
-                DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
+                AmbientStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
                 mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF,
                 R.string.priority_mode_dream_overlay_content_description);
     }
 
-    private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show,
+    private void showIcon(@AmbientStatusBarView.StatusIconType int iconType, boolean show,
             int contentDescriptionResId) {
         showIcon(iconType, show, mResources.getString(contentDescriptionResId));
     }
 
     private void showIcon(
-            @DreamOverlayStatusBarView.StatusIconType int iconType,
+            @AmbientStatusBarView.StatusIconType int iconType,
             boolean show,
             @Nullable String contentDescription) {
         mMainExecutor.execute(() -> {
             if (mIsAttached) {
                 mLogger.logShowOrHideStatusBarItem(
-                        show, DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
+                        show, AmbientStatusBarView.getLoggableStatusIconType(iconType));
                 mView.showIcon(iconType, show, contentDescription);
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
index 9ef9938..fcd7ef5 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
@@ -18,11 +18,15 @@
 
 import static com.android.systemui.ambient.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT;
 
+import android.app.DreamManager;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Flags;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 
@@ -38,28 +42,39 @@
 public class ShadeTouchHandler implements TouchHandler {
     private final Optional<CentralSurfaces> mSurfaces;
     private final ShadeViewController mShadeViewController;
+    private final DreamManager mDreamManager;
     private final int mInitiationHeight;
 
+    /**
+     * Tracks whether or not we are capturing a given touch. Will be null before and after a touch.
+     */
+    private Boolean mCapture;
+
     @Inject
     ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces,
             ShadeViewController shadeViewController,
+            DreamManager dreamManager,
             @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) {
         mSurfaces = centralSurfaces;
         mShadeViewController = shadeViewController;
+        mDreamManager = dreamManager;
         mInitiationHeight = initiationHeight;
     }
 
     @Override
     public void onSessionStart(TouchSession session) {
-        if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
+        if (mSurfaces.isEmpty()) {
             session.pop();
             return;
         }
 
-        session.registerInputListener(ev -> {
-            mShadeViewController.handleExternalTouch((MotionEvent) ev);
+        session.registerCallback(() -> mCapture = null);
 
+        session.registerInputListener(ev -> {
             if (ev instanceof MotionEvent) {
+                if (mCapture != null && mCapture) {
+                    sendTouchEvent((MotionEvent) ev);
+                }
                 if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
                     session.pop();
                 }
@@ -68,19 +83,41 @@
 
         session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() {
             @Override
-            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+            public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2, float distanceX,
                     float distanceY) {
-                return true;
+                if (mCapture == null) {
+                    // Only capture swipes that are going downwards.
+                    mCapture = Math.abs(distanceY) > Math.abs(distanceX) && distanceY < 0;
+                    if (mCapture) {
+                        // Send the initial touches over, as the input listener has already
+                        // processed these touches.
+                        sendTouchEvent(e1);
+                        sendTouchEvent(e2);
+                    }
+                }
+                return mCapture;
             }
 
             @Override
-            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+            public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2, float velocityX,
                     float velocityY) {
-                return true;
+                return mCapture;
             }
         });
     }
 
+    private void sendTouchEvent(MotionEvent event) {
+        if (Flags.communalHub() && !mDreamManager.isDreaming()) {
+            // Send touches to central surfaces only when on the glanceable hub while not dreaming.
+            // While sending touches where while dreaming will open the shade, the shade
+            // while closing if opened then closed in the same gesture.
+            mSurfaces.get().handleExternalShadeWindowTouch(event);
+        } else {
+            // Send touches to the shade view when dreaming.
+            mShadeViewController.handleExternalTouch(event);
+        }
+    }
+
     @Override
     public void getTouchInitiationRegion(Rect bounds, Region region, Rect exclusionRect) {
         final Rect outBounds = new Rect(bounds);
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
index 10a0d95..8a9a322 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
@@ -4,6 +4,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.BatterySaverTile
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileDataInteractor
 import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileUserActionInteractor
@@ -28,6 +29,13 @@
     @StringKey(BatterySaverTile.TILE_SPEC)
     fun bindBatterySaverTile(batterySaverTile: BatterySaverTile): QSTileImpl<*>
 
+    @Binds
+    @IntoMap
+    @StringKey(BATTERY_SAVER_TILE_SPEC)
+    fun provideBatterySaverAvailabilityInteractor(
+            impl: BatterySaverTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
     companion object {
         private const val BATTERY_SAVER_TILE_SPEC = "battery"
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 177aad9..430ff07 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -515,7 +515,9 @@
         } else {
             throw new IllegalStateException("Unknown credential type: " + credentialType);
         }
-        mCredentialView = factory.inflate(layoutResourceId, null, false);
+        // TODO(b/288175645): Once AuthContainerView is removed, set 0dp in credential view xml
+        //  files with the corresponding left/right or top/bottom constraints being set to "parent".
+        mCredentialView = factory.inflate(layoutResourceId, mLayout, false);
 
         // The background is used for detecting taps / cancelling authentication. Since the
         // credential view is full-screen and should not be canceled from background taps,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt
deleted file mode 100644
index cf699a2..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START
-import android.hardware.biometrics.BiometricSourceType
-import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
-import android.util.Log
-import com.android.app.tracing.TraceStateLogger
-import com.android.internal.util.LatencyTracker
-import com.android.internal.util.LatencyTracker.ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import javax.inject.Inject
-
-private const val TAG = "FpsUnlockTracker"
-private const val TRACE_COUNTER_NAME = "FpsUnlockStage"
-private const val TRACE_TAG_AOD = "AOD"
-private const val TRACE_TAG_KEYGUARD = "KEYGUARD"
-private const val DEBUG = true
-
-/** This is a class for monitoring unlock latency of fps and logging stages in perfetto. */
-@SysUISingleton
-class FpsUnlockTracker
-@Inject
-constructor(
-    private val statusBarStateController: StatusBarStateController,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-    private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
-    private val latencyTracker: LatencyTracker,
-) {
-    private val fpsTraceStateLogger = TraceStateLogger(TRACE_COUNTER_NAME)
-    private var fpsAuthenticated: Boolean = false
-
-    private val keyguardUpdateMonitorCallback =
-        object : KeyguardUpdateMonitorCallback() {
-            override fun onBiometricAcquired(
-                biometricSourceType: BiometricSourceType?,
-                acquireInfo: Int
-            ) {
-                if (keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) {
-                    onHalAuthenticationStage(acquireInfo)
-                }
-            }
-
-            override fun onBiometricAuthenticated(
-                userId: Int,
-                biometricSourceType: BiometricSourceType?,
-                isStrongBiometric: Boolean
-            ) {
-                if (biometricSourceType == FINGERPRINT) {
-                    fpsAuthenticated = true
-                    onExitKeyguard()
-                }
-            }
-
-            override fun onBiometricError(
-                msgId: Int,
-                errString: String?,
-                biometricSourceType: BiometricSourceType?
-            ) {
-                if (biometricSourceType == FINGERPRINT) {
-                    latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
-                }
-            }
-
-            override fun onBiometricRunningStateChanged(
-                running: Boolean,
-                biometricSourceType: BiometricSourceType?
-            ) {
-                if (biometricSourceType != FINGERPRINT || !running) {
-                    return
-                }
-                onWaitForAuthenticationStage()
-            }
-        }
-
-    private val keyguardUnlockAnimationListener =
-        object : KeyguardUnlockAnimationListener {
-            override fun onUnlockAnimationFinished() = onUnlockedStage()
-        }
-
-    /** Start tracking the fps unlock. */
-    fun startTracking() {
-        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
-        keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
-            keyguardUnlockAnimationListener
-        )
-    }
-
-    /** Stop tracking the fps unlock. */
-    fun stopTracking() {
-        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
-        keyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
-            keyguardUnlockAnimationListener
-        )
-    }
-
-    /**
-     * The stage when the devices is locked and is possible to be unlocked via fps. However, in some
-     * situations, it might be unlocked only via bouncer.
-     */
-    fun onWaitForAuthenticationStage() {
-        val stage =
-            if (keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
-                FpsUnlockStage.WAIT_FOR_AUTHENTICATION.name
-            else FpsUnlockStage.WAIT_FOR_AUTHENTICATION.name + "(Not allowed)"
-        fpsTraceStateLogger.log(stage)
-        if (DEBUG) {
-            Log.d(TAG, "onWaitForAuthenticationStage: stage=$stage")
-        }
-    }
-
-    /**
-     * The stage dedicated to UDFPS, SFPS should not enter this stage. The only place where invokes
-     * this function is UdfpsController#onFingerDown.
-     */
-    fun onUiReadyStage() {
-        if (!keyguardUpdateMonitor.isUdfpsSupported || !keyguardUpdateMonitor.isUdfpsEnrolled) {
-            return
-        }
-        fpsTraceStateLogger.log(FpsUnlockStage.UI_READY.name)
-        startLatencyTracker()
-        if (DEBUG) {
-            Log.d(TAG, "onUiReadyStage: dozing=${statusBarStateController.isDozing}")
-        }
-    }
-
-    /** The stage when the HAL is authenticating the fingerprint. */
-    fun onHalAuthenticationStage(acquire: Int) {
-        fpsTraceStateLogger.log("${FpsUnlockStage.HAL_AUTHENTICATION.name}($acquire)")
-        // Start latency tracker here only for SFPS, UDFPS should start at onUiReadyStage.
-        if (
-            keyguardUpdateMonitor.isSfpsSupported &&
-                keyguardUpdateMonitor.isSfpsEnrolled &&
-                acquire == FINGERPRINT_ACQUIRED_START
-        ) {
-            startLatencyTracker()
-        }
-        if (DEBUG) {
-            Log.d(
-                TAG,
-                "onHalAuthenticationStage: acquire=$acquire" +
-                    ", sfpsSupported=${keyguardUpdateMonitor.isSfpsSupported}" +
-                    ", sfpsEnrolled=${keyguardUpdateMonitor.isSfpsEnrolled}"
-            )
-        }
-    }
-
-    /** The stage when the authentication is succeeded and is going to exit keyguard. */
-    fun onExitKeyguard() {
-        fpsTraceStateLogger.log(FpsUnlockStage.EXIT_KEYGUARD.name)
-        if (DEBUG) {
-            Log.d(TAG, "onExitKeyguard: fpsAuthenticated=$fpsAuthenticated")
-        }
-    }
-
-    /**
-     * The stage when the unlock animation is finished which means the user can start interacting
-     * with the device.
-     */
-    fun onUnlockedStage() {
-        fpsTraceStateLogger.log(FpsUnlockStage.UNLOCKED.name)
-        if (fpsAuthenticated) {
-            // The device is unlocked successfully via fps, end the instrument.
-            latencyTracker.onActionEnd(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
-        } else {
-            // The device is unlocked but not via fps, maybe bouncer? Cancel the instrument.
-            latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
-        }
-        if (DEBUG) {
-            Log.d(TAG, "onUnlockedStage: fpsAuthenticated=$fpsAuthenticated")
-        }
-        fpsAuthenticated = false
-    }
-
-    private fun startLatencyTracker() {
-        latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
-        val tag = if (statusBarStateController.isDozing) TRACE_TAG_AOD else TRACE_TAG_KEYGUARD
-        latencyTracker.onActionStart(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, tag)
-    }
-}
-
-private enum class FpsUnlockStage {
-    WAIT_FOR_AUTHENTICATION,
-    UI_READY,
-    HAL_AUTHENTICATION,
-    EXIT_KEYGUARD,
-    UNLOCKED
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 85b5faf..ad142a8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -184,7 +184,6 @@
     @NonNull private final InputManager mInputManager;
     @NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
     @NonNull private final SelectedUserInteractor mSelectedUserInteractor;
-    @NonNull private final FpsUnlockTracker mFpsUnlockTracker;
     private final boolean mIgnoreRefreshRate;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
 
@@ -712,7 +711,6 @@
             @NonNull DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
             @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
             @NonNull SelectedUserInteractor selectedUserInteractor,
-            @NonNull FpsUnlockTracker fpsUnlockTracker,
             @NonNull KeyguardTransitionInteractor keyguardTransitionInteractor,
             Lazy<DeviceEntryUdfpsTouchOverlayViewModel> deviceEntryUdfpsTouchOverlayViewModel,
             Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel,
@@ -765,8 +763,6 @@
         mInputManager = inputManager;
         mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
         mSelectedUserInteractor = selectedUserInteractor;
-        mFpsUnlockTracker = fpsUnlockTracker;
-        mFpsUnlockTracker.startTracking();
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
 
         mTouchProcessor = singlePointerTouchProcessor;
@@ -1067,9 +1063,6 @@
         if (isOptical()) {
             mLatencyTracker.onActionStart(ACTION_UDFPS_ILLUMINATE);
         }
-        if (getBiometricSessionType() == SESSION_KEYGUARD) {
-            mFpsUnlockTracker.onUiReadyStage();
-        }
         // Refresh screen timeout and boost process priority if possible.
         mPowerManager.userActivity(mSystemClock.uptimeMillis(),
                 PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 298b87d..c3d9240 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -39,7 +39,6 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
-import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
@@ -59,7 +58,6 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.launch
 
 /** Class that coordinates non-HBM animations during keyguard authentication. */
@@ -307,27 +305,12 @@
     @VisibleForTesting
     suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job {
         return scope.launch {
-            transitionInteractor.dozeAmountTransition.collect { transitionStep ->
-                if (
-                    transitionStep.from == AOD &&
-                        transitionStep.transitionState == TransitionState.CANCELED
-                ) {
-                    if (transitionInteractor.startedKeyguardTransitionStep.first().to != AOD) {
-                        // If the next started transition isn't transitioning back to AOD, force
-                        // doze amount to be 0f (as if the transition to the lockscreen completed).
-                        view.onDozeAmountChanged(
-                            0f,
-                            0f,
-                            UdfpsKeyguardViewLegacy.ANIMATION_NONE,
-                        )
-                    }
-                } else {
-                    view.onDozeAmountChanged(
-                        transitionStep.value,
-                        transitionStep.value,
-                        UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
-                    )
-                }
+            transitionInteractor.transitionValue(AOD).collect {
+                view.onDozeAmountChanged(
+                    it,
+                    it,
+                    UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
+                )
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
index 14d8caf..e2a8a69 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
@@ -39,12 +39,14 @@
 import com.android.systemui.biometrics.udfps.BoundingBoxOverlapDetector
 import com.android.systemui.biometrics.udfps.EllipseOverlapDetector
 import com.android.systemui.biometrics.udfps.OverlapDetector
+import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
 import com.android.systemui.biometrics.ui.binder.SideFpsOverlayViewBinder
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.ui.binder.AlternateBouncerViewBinder
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import com.android.systemui.util.concurrency.ThreadFactory
 import dagger.Binds
+import dagger.BindsOptionalOf
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.ClassKey
@@ -101,6 +103,9 @@
     @SysUISingleton
     fun displayStateRepository(impl: DisplayStateRepositoryImpl): DisplayStateRepository
 
+    @BindsOptionalOf
+    fun deviceEntryUnlockTrackerViewBinder(): DeviceEntryUnlockTrackerViewBinder
+
     companion object {
         /** Background [Executor] for HAL related operations. */
         @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index 9b14d6f..2fa4a89 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -139,9 +139,10 @@
             }
             .stateIn(
                 backgroundScope,
-                started = SharingStarted.WhileSubscribed(),
+                started = SharingStarted.Eagerly,
                 initialValue = false,
             )
+
     private fun dpiFromPx(size: Float, densityDpi: Int): Float {
         val densityRatio = densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT
         return size / densityRatio
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 4f96c1e..348b423 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -40,8 +40,7 @@
             operationInfo = operationInfo,
             showEmergencyCallButton = info.isShowEmergencyCallButton
         ) {
-        val logoRes: Int = info.logoRes
-        val logoBitmap: Bitmap? = info.logoBitmap
+        val logoBitmap: Bitmap? = info.logo
         val logoDescription: String? = info.logoDescription
         val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
         val componentNameForConfirmDeviceCredentialActivity: ComponentName? =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 65c5b6b..408e2c2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -457,17 +457,10 @@
 
                 // Retry and confirmation when finger on sensor
                 launch {
-                    combine(
-                            viewModel.canTryAgainNow,
-                            viewModel.hasFingerOnSensor,
-                            viewModel.isPendingConfirmation,
-                            ::Triple
-                        )
-                        .collect { (canRetry, fingerAcquired, pendingConfirmation) ->
+                    combine(viewModel.canTryAgainNow, viewModel.hasFingerOnSensor, ::Pair)
+                        .collect { (canRetry, fingerAcquired) ->
                             if (canRetry && fingerAcquired) {
                                 legacyCallback.onButtonTryAgain()
-                            } else if (pendingConfirmation && fingerAcquired) {
-                                viewModel.confirmAuthenticated()
                             }
                         }
                 }
@@ -497,13 +490,21 @@
     @Deprecated("TODO(b/330788871): remove after replacing AuthContainerView")
     interface Callback {
         fun onAuthenticated()
+
         fun onUserCanceled()
+
         fun onButtonNegative()
+
         fun onButtonTryAgain()
+
         fun onContentViewMoreOptionsButtonPressed()
+
         fun onError()
+
         fun onUseDeviceCredential()
+
         fun onStartDelayedFingerprintSensor()
+
         fun onAuthenticatedAndConfirmed()
     }
 
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 628b533..a87ee24 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
@@ -51,6 +51,7 @@
 import com.android.systemui.biometrics.ui.viewmodel.isMedium
 import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
 import com.android.systemui.biometrics.ui.viewmodel.isSmall
+import com.android.systemui.biometrics.ui.viewmodel.isTop
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import kotlin.math.abs
@@ -100,13 +101,13 @@
             val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
             val panelView = view.requireViewById<View>(R.id.panel)
             val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
-            val cornerRadiusPx =
+            val pxToDp =
                 TypedValue.applyDimension(
-                        TypedValue.COMPLEX_UNIT_DIP,
-                        cornerRadius,
-                        view.resources.displayMetrics
-                    )
-                    .toInt()
+                    TypedValue.COMPLEX_UNIT_DIP,
+                    1f,
+                    view.resources.displayMetrics
+                )
+            val cornerRadiusPx = (pxToDp * cornerRadius).toInt()
 
             var currentSize: PromptSize? = null
             var currentPosition: PromptPosition = PromptPosition.Bottom
@@ -132,18 +133,10 @@
                                     cornerRadiusPx.toFloat()
                                 )
                             }
+                            PromptPosition.Bottom,
                             PromptPosition.Top -> {
                                 outline.setRoundRect(
                                     0,
-                                    -cornerRadiusPx,
-                                    view.width,
-                                    view.height,
-                                    cornerRadiusPx.toFloat()
-                                )
-                            }
-                            PromptPosition.Bottom -> {
-                                outline.setRoundRect(
-                                    0,
                                     0,
                                     view.width,
                                     view.height + cornerRadiusPx,
@@ -308,6 +301,7 @@
                             }
                         }
                     }
+
                     lifecycleScope.launch {
                         viewModel.iconSize.collect { iconSize ->
                             iconHolderView.layoutParams.width = iconSize.first
@@ -370,7 +364,7 @@
                             if (midGuideline != null) {
                                 val left =
                                     if (bounds.left >= 0) {
-                                        bounds.left
+                                        abs(bounds.left)
                                     } else {
                                         view.width - abs(bounds.left)
                                     }
@@ -378,13 +372,14 @@
                                     if (bounds.right >= 0) {
                                         view.width - abs(bounds.right)
                                     } else {
-                                        bounds.right
+                                        abs(bounds.right)
                                     }
                                 val mid = (left + right) / 2
                                 mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid)
                             }
                         }
                     }
+
                     lifecycleScope.launch {
                         combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect {
                             (hideSensorIcon, size) ->
@@ -415,6 +410,33 @@
                                     R.id.rightGuideline,
                                     ConstraintSet.RIGHT
                                 )
+                            } else if (position.isTop) {
+                                // Top position is only used for 180 rotation Udfps
+                                // Requires repositioning due to sensor location at top of screen
+                                mediumConstraintSet.connect(
+                                    R.id.scrollView,
+                                    ConstraintSet.TOP,
+                                    R.id.indicator,
+                                    ConstraintSet.BOTTOM
+                                )
+                                mediumConstraintSet.connect(
+                                    R.id.scrollView,
+                                    ConstraintSet.BOTTOM,
+                                    R.id.button_bar,
+                                    ConstraintSet.TOP
+                                )
+                                mediumConstraintSet.connect(
+                                    R.id.panel,
+                                    ConstraintSet.TOP,
+                                    R.id.biometric_icon,
+                                    ConstraintSet.TOP
+                                )
+                                mediumConstraintSet.setMargin(
+                                    R.id.panel,
+                                    ConstraintSet.TOP,
+                                    (-24 * pxToDp).toInt()
+                                )
+                                mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f)
                             }
 
                             when {
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/DeviceEntryUnlockTrackerViewBinder.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
copy to packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/DeviceEntryUnlockTrackerViewBinder.kt
index 2e9169e..78eaab2 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/DeviceEntryUnlockTrackerViewBinder.kt
@@ -13,17 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.bluetooth.qsdialog
 
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
+package com.android.systemui.biometrics.ui.binder
 
-@Module
-interface BluetoothTileDialogModule {
-    @Binds
-    @SysUISingleton
-    fun bindDeviceItemActionInteractor(
-        impl: DeviceItemActionInteractorImpl
-    ): DeviceItemActionInteractor
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+
+/** ViewBinder for device entry unlock tracker implemented in vendor */
+interface DeviceEntryUnlockTrackerViewBinder {
+    /**
+     *  Allows vendor binds vendor's view model to the specified view.
+     *  @param view the view to be bound
+     */
+    fun bind(view: KeyguardRootView) {}
 }
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 ff7ac35..9cc4650 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,7 +33,6 @@
 import com.android.app.animation.Interpolators
 import com.android.keyguard.KeyguardPINView
 import com.android.systemui.CoreStartable
-import com.android.systemui.biometrics.FpsUnlockTracker
 import com.android.systemui.biometrics.domain.interactor.BiometricStatusInteractor
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
@@ -65,7 +64,6 @@
     private val biometricStatusInteractor: Lazy<BiometricStatusInteractor>,
     private val displayStateInteractor: Lazy<DisplayStateInteractor>,
     private val deviceEntrySideFpsOverlayInteractor: Lazy<DeviceEntrySideFpsOverlayInteractor>,
-    private val fpsUnlockTracker: Lazy<FpsUnlockTracker>,
     private val layoutInflater: Lazy<LayoutInflater>,
     private val sideFpsProgressBarViewModel: Lazy<SideFpsProgressBarViewModel>,
     private val sfpsSensorInteractor: Lazy<SideFpsSensorInteractor>,
@@ -114,7 +112,6 @@
                     }
                 }
             }
-            .invokeOnCompletion { fpsUnlockTracker.get().stopTracking() }
     }
 
     private var overlayView: View? = null
@@ -138,7 +135,7 @@
                 displayStateInteractor.get(),
                 sfpsSensorInteractor.get(),
             )
-        bind(overlayView!!, overlayViewModel, fpsUnlockTracker.get(), windowManager.get())
+        bind(overlayView!!, overlayViewModel, windowManager.get())
         overlayView!!.visibility = View.INVISIBLE
         Log.d(TAG, "show(): adding overlayView $overlayView")
         windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
@@ -163,12 +160,9 @@
         fun bind(
             overlayView: View,
             viewModel: SideFpsOverlayViewModel,
-            fpsUnlockTracker: FpsUnlockTracker,
             windowManager: WindowManager
         ) {
             overlayView.repeatWhenAttached {
-                fpsUnlockTracker.startTracking()
-
                 val lottie = it.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
                 lottie.addLottieOnCompositionLoadedListener { composition: LottieComposition ->
                     if (overlayView.visibility != View.VISIBLE) {
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 a39a74f..ac8807d 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
@@ -30,6 +30,7 @@
 import android.hardware.biometrics.Flags.customBiometricPrompt
 import android.hardware.biometrics.PromptContentView
 import android.os.UserHandle
+import android.text.TextPaint
 import android.util.Log
 import android.util.RotationUtils
 import android.view.HapticFeedbackConstants
@@ -52,6 +53,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
 import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.combine
 import javax.inject.Inject
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.coroutineScope
@@ -230,7 +232,6 @@
     val fingerprintStartMode: Flow<FingerprintStartMode> = _fingerprintStartMode.asStateFlow()
 
     /** Whether a finger has been acquired by the sensor */
-    // TODO(b/331948073): Add support for detecting SFPS finger without authentication running
     val hasFingerBeenAcquired: Flow<Boolean> =
         combine(biometricStatusInteractor.fingerprintAcquiredStatus, modalities) {
                 status,
@@ -260,17 +261,19 @@
     val position: Flow<PromptPosition> =
         combine(
                 _forceLargeSize,
+                promptKind,
                 displayStateInteractor.isLargeScreen,
                 displayStateInteractor.currentRotation,
-            ) { forceLarge, isLargeScreen, rotation ->
+                modalities
+            ) { forceLarge, promptKind, isLargeScreen, rotation, modalities ->
                 when {
                     forceLarge ||
                         isLargeScreen ||
-                        promptKind.value.isOnePaneNoSensorLandscapeBiometric() ->
-                        PromptPosition.Bottom
+                        promptKind.isOnePaneNoSensorLandscapeBiometric() -> PromptPosition.Bottom
                     rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right
                     rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left
-                    rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top
+                    rotation == DisplayRotation.ROTATION_180 && modalities.hasUdfps ->
+                        PromptPosition.Top
                     else -> PromptPosition.Bottom
                 }
             }
@@ -306,6 +309,10 @@
         context.resources.getDimensionPixelSize(
             R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding
         )
+    private val udfpsHorizontalShorterGuidelinePadding =
+        context.resources.getDimensionPixelSize(
+            R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding
+        )
     private val mediumTopGuidelinePadding =
         context.resources.getDimensionPixelSize(
             R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding
@@ -362,7 +369,14 @@
                                 landscapeMediumBottomPadding
                             )
                         }
-                    PromptPosition.Top -> Rect()
+                    PromptPosition.Top ->
+                        if (size.isSmall) {
+                            Rect(0, 0, 0, portraitSmallBottomPadding)
+                        } else if (size.isMedium && modalities.hasUdfps) {
+                            Rect(0, 0, 0, sensorBounds.bottom)
+                        } else {
+                            Rect(0, 0, 0, portraitMediumBottomPadding)
+                        }
                 }
             }
             .distinctUntilChanged()
@@ -440,47 +454,6 @@
             }
         }
 
-    /**
-     * Rect for positioning prompt guidelines (left, top, right, unused)
-     *
-     * Negative values are used to signify that guideline measuring should be flipped, measuring
-     * from opposite side of the screen
-     */
-    val guidelineBounds: Flow<Rect> =
-        combine(iconPosition, promptKind, size, position, modalities) {
-                _,
-                promptKind,
-                size,
-                position,
-                modalities ->
-                when (position) {
-                    PromptPosition.Bottom ->
-                        if (promptKind.isOnePaneNoSensorLandscapeBiometric()) {
-                            Rect(0, 0, 0, 0)
-                        } else {
-                            Rect(0, mediumTopGuidelinePadding, 0, 0)
-                        }
-                    PromptPosition.Right ->
-                        if (size.isSmall) {
-                            Rect(-smallHorizontalGuidelinePadding, 0, 0, 0)
-                        } else if (modalities.hasUdfps) {
-                            Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0)
-                        } else {
-                            Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0)
-                        }
-                    PromptPosition.Left ->
-                        if (size.isSmall) {
-                            Rect(0, 0, -smallHorizontalGuidelinePadding, 0)
-                        } else if (modalities.hasUdfps) {
-                            Rect(0, 0, udfpsHorizontalGuidelinePadding, 0)
-                        } else {
-                            Rect(0, 0, -mediumHorizontalGuidelinePadding, 0)
-                        }
-                    PromptPosition.Top -> Rect()
-                }
-            }
-            .distinctUntilChanged()
-
     /** Padding for prompt UI elements */
     val promptPadding: Flow<Rect> =
         combine(size, displayStateInteractor.currentRotation) { size, rotation ->
@@ -504,7 +477,6 @@
             .map {
                 when {
                     !(customBiometricPrompt() && constraintBp()) || it == null -> null
-                    it.logoRes != -1 -> context.resources.getDrawable(it.logoRes, context.theme)
                     it.logoBitmap != null -> BitmapDrawable(context.resources, it.logoBitmap)
                     else -> context.getUserBadgedIcon(it, iconProvider, activityTaskManager)
                 }
@@ -548,6 +520,81 @@
             if (contentView == null) description else ""
         }
 
+    private val hasOnlyOneLineTitle: Flow<Boolean> =
+        combine(title, subtitle, contentView, description) {
+            title,
+            subtitle,
+            contentView,
+            description ->
+            if (subtitle.isNotEmpty() || contentView != null || description.isNotEmpty()) {
+                false
+            } else {
+                val maxWidth =
+                    context.resources.getDimensionPixelSize(
+                        R.dimen.biometric_prompt_two_pane_udfps_shorter_content_width
+                    )
+                val attributes =
+                    context.obtainStyledAttributes(
+                        R.style.TextAppearance_AuthCredential_Title,
+                        intArrayOf(android.R.attr.textSize)
+                    )
+                val paint = TextPaint()
+                paint.textSize = attributes.getDimensionPixelSize(0, 0).toFloat()
+                val textWidth = paint.measureText(title)
+                attributes.recycle()
+                textWidth / maxWidth <= 1
+            }
+        }
+
+    /**
+     * Rect for positioning prompt guidelines (left, top, right, unused)
+     *
+     * Negative values are used to signify that guideline measuring should be flipped, measuring
+     * from opposite side of the screen
+     */
+    val guidelineBounds: Flow<Rect> =
+        combine(iconPosition, promptKind, size, position, modalities, hasOnlyOneLineTitle) {
+                _,
+                promptKind,
+                size,
+                position,
+                modalities,
+                hasOnlyOneLineTitle ->
+                var left = 0
+                var top = 0
+                var right = 0
+                when (position) {
+                    PromptPosition.Bottom -> {
+                        val noSensorLandscape = promptKind.isOnePaneNoSensorLandscapeBiometric()
+                        top = if (noSensorLandscape) 0 else mediumTopGuidelinePadding
+                    }
+                    PromptPosition.Right ->
+                        left = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle)
+                    PromptPosition.Left ->
+                        right = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle)
+                    PromptPosition.Top -> {}
+                }
+                Rect(left, top, right, 0)
+            }
+            .distinctUntilChanged()
+
+    private fun getHorizontalPadding(
+        size: PromptSize,
+        modalities: BiometricModalities,
+        hasOnlyOneLineTitle: Boolean
+    ) =
+        if (size.isSmall) {
+            -smallHorizontalGuidelinePadding
+        } else if (modalities.hasUdfps) {
+            if (hasOnlyOneLineTitle) {
+                -udfpsHorizontalShorterGuidelinePadding
+            } else {
+                udfpsHorizontalGuidelinePadding
+            }
+        } else {
+            -mediumHorizontalGuidelinePadding
+        }
+
     /** If the indicator (help, error) message should be shown. */
     val isIndicatorMessageVisible: Flow<Boolean> =
         combine(
@@ -569,7 +616,8 @@
         }
 
     /** If the icon can be used as a confirmation button. */
-    val isIconConfirmButton: Flow<Boolean> = size.map { it.isNotSmall }.distinctUntilChanged()
+    val isIconConfirmButton: Flow<Boolean> =
+        combine(modalities, size) { modalities, size -> modalities.hasUdfps && size.isNotSmall }
 
     /** If the negative button should be shown. */
     val isNegativeButtonVisible: Flow<Boolean> =
@@ -652,6 +700,9 @@
         failedModality: BiometricModality = BiometricModality.None,
     ) = coroutineScope {
         if (_isAuthenticated.value.isAuthenticated) {
+            if (_isAuthenticated.value.needsUserConfirmation && hapticFeedback) {
+                vibrateOnError()
+            }
             return@coroutineScope
         }
 
@@ -775,6 +826,14 @@
         helpMessage: String = "",
     ) {
         if (_isAuthenticated.value.isAuthenticated) {
+            // Treat second authentication with a different modality as confirmation for the first
+            if (
+                _isAuthenticated.value.needsUserConfirmation &&
+                    modality != _isAuthenticated.value.authenticatedModality
+            ) {
+                confirmAuthenticated()
+                return
+            }
             // TODO(jbolinger): convert to go/tex-apc?
             Log.w(TAG, "Cannot show authenticated after authenticated")
             return
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
index 81fe2a5..7f1cb5d 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepository.kt
@@ -97,6 +97,10 @@
                 // Server could throw TimeoutException, InterruptedException or ExecutionException
                 Log.e(TAG, "Error calling isAutoOnSupported", e)
                 false
+            } catch (e: NoSuchMethodError) {
+                // TODO(b/346716614): Remove this when the flag is cleaned up.
+                Log.e(TAG, "Non-existed api isAutoOnSupported", e)
+                false
             }
         }
 
@@ -109,6 +113,9 @@
                 // Server could throw IllegalStateException, TimeoutException, InterruptedException
                 // or ExecutionException
                 Log.e(TAG, "Error calling setAutoOnEnabled", e)
+            } catch (e: NoSuchMethodError) {
+                // TODO(b/346716614): Remove this when the flag is cleaned up.
+                Log.e(TAG, "Non-existed api setAutoOn", e)
             }
         }
     }
@@ -122,6 +129,10 @@
                 // or ExecutionException
                 Log.e(TAG, "Error calling isAutoOnEnabled", e)
                 false
+            } catch (e: NoSuchMethodError) {
+                // TODO(b/346716614): Remove this when the flag is cleaned up.
+                Log.e(TAG, "Non-existed api isAutoOnEnabled", e)
+                false
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
index c30aea0..72312b8 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
+import android.bluetooth.BluetoothDevice
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel.DEBUG
 import com.android.systemui.log.dagger.BluetoothTileDialogLog
@@ -103,4 +104,29 @@
 
     fun logDeviceUiUpdate(duration: Long) =
         logBuffer.log(TAG, DEBUG, { long1 = duration }, { "DeviceUiUpdate. duration=$long1" })
+
+    fun logDeviceClickInAudioSharingWhenEnabled(inAudioSharing: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { str1 = inAudioSharing.toString() },
+            { "DeviceClick. in audio sharing=$str1" }
+        )
+    }
+
+    fun logConnectedLeByGroupId(map: Map<Int, List<BluetoothDevice>>) {
+        logBuffer.log(TAG, DEBUG, { str1 = map.toString() }, { "ConnectedLeByGroupId. map=$str1" })
+    }
+
+    fun logLaunchSettingsCriteriaMatched(criteria: String, deviceItem: DeviceItem) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = criteria
+                str2 = deviceItem.toString()
+            },
+            { "$str1. deviceItem=$str2" }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
index b592b8e..4a358c0 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
@@ -35,7 +35,17 @@
     CONNECTED_OTHER_DEVICE_DISCONNECT(1508),
     @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617),
     @UiEvent(doc = "The audio sharing button is clicked")
-    BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700);
+    BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700),
+    @UiEvent(doc = "Currently broadcasting and a LE audio supported device is clicked")
+    LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED(1717),
+    @UiEvent(doc = "Currently broadcasting and a non-LE audio supported device is clicked")
+    LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED(1718),
+    @UiEvent(
+        doc = "Not broadcasting, having one connected, another saved LE audio device is clicked"
+    )
+    LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED(1719),
+    @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked")
+    LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720);
 
     override fun getId() = metricId
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
index 9311760..4dafa93 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
@@ -16,32 +16,87 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothProfile
+import android.content.Intent
+import android.os.Bundle
+import android.provider.Settings
 import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.bluetooth.A2dpProfile
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.HeadsetProfile
+import com.android.settingslib.bluetooth.HearingAidProfile
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor.LaunchSettingsCriteria.Companion.getCurrentConnectedLeByGroupId
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.withContext
 
-/** Defines interface for click handling of a DeviceItem. */
-interface DeviceItemActionInteractor {
-    suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog)
-}
-
 @SysUISingleton
-open class DeviceItemActionInteractorImpl
+class DeviceItemActionInteractor
 @Inject
 constructor(
+    private val activityStarter: ActivityStarter,
+    private val dialogTransitionAnimator: DialogTransitionAnimator,
+    private val localBluetoothManager: LocalBluetoothManager?,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
     private val logger: BluetoothTileDialogLogger,
     private val uiEventLogger: UiEventLogger,
-) : DeviceItemActionInteractor {
+) {
+    private val leAudioProfile: LeAudioProfile?
+        get() = localBluetoothManager?.profileManager?.leAudioProfile
 
-    override suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
+    private val assistantProfile: LocalBluetoothLeBroadcastAssistant?
+        get() = localBluetoothManager?.profileManager?.leAudioBroadcastAssistantProfile
+
+    private val launchSettingsCriteriaList: List<LaunchSettingsCriteria>
+        get() =
+            listOf(
+                InSharingClickedNoSource(localBluetoothManager, backgroundDispatcher, logger),
+                NotSharingClickedNonConnect(
+                    leAudioProfile,
+                    assistantProfile,
+                    backgroundDispatcher,
+                    logger
+                ),
+                NotSharingClickedConnected(
+                    leAudioProfile,
+                    assistantProfile,
+                    backgroundDispatcher,
+                    logger
+                )
+            )
+
+    suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) {
         withContext(backgroundDispatcher) {
             logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type)
+            if (
+                BluetoothUtils.isAudioSharingEnabled() &&
+                    localBluetoothManager != null &&
+                    leAudioProfile != null &&
+                    assistantProfile != null
+            ) {
+                val inAudioSharing = BluetoothUtils.isBroadcasting(localBluetoothManager)
+                logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing)
 
+                val criteriaMatched =
+                    launchSettingsCriteriaList.firstOrNull {
+                        it.matched(inAudioSharing, deviceItem)
+                    }
+                if (criteriaMatched != null) {
+                    uiEventLogger.log(criteriaMatched.getClickUiEvent(deviceItem))
+                    launchSettings(deviceItem.cachedBluetoothDevice.device, dialog)
+                    return@withContext
+                }
+            }
             deviceItem.cachedBluetoothDevice.apply {
                 when (deviceItem.type) {
                     DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> {
@@ -69,4 +124,184 @@
             }
         }
     }
+
+    private fun launchSettings(device: BluetoothDevice, dialog: SystemUIDialog) {
+        val intent =
+            Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply {
+                putExtra(
+                    EXTRA_SHOW_FRAGMENT_ARGUMENTS,
+                    Bundle().apply {
+                        putParcelable(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device)
+                    }
+                )
+            }
+        intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
+        activityStarter.postStartActivityDismissingKeyguard(
+            intent,
+            0,
+            dialogTransitionAnimator.createActivityTransitionController(dialog)
+        )
+    }
+
+    private interface LaunchSettingsCriteria {
+        suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean
+
+        suspend fun getClickUiEvent(deviceItem: DeviceItem): BluetoothTileDialogUiEvent
+
+        companion object {
+            suspend fun getCurrentConnectedLeByGroupId(
+                leAudioProfile: LeAudioProfile,
+                assistantProfile: LocalBluetoothLeBroadcastAssistant,
+                @Background backgroundDispatcher: CoroutineDispatcher,
+                logger: BluetoothTileDialogLogger,
+            ): Map<Int, List<BluetoothDevice>> {
+                return withContext(backgroundDispatcher) {
+                    assistantProfile
+                        .getDevicesMatchingConnectionStates(
+                            intArrayOf(BluetoothProfile.STATE_CONNECTED)
+                        )
+                        ?.filterNotNull()
+                        ?.groupBy { leAudioProfile.getGroupId(it) }
+                        ?.also { logger.logConnectedLeByGroupId(it) } ?: emptyMap()
+                }
+            }
+        }
+    }
+
+    private class InSharingClickedNoSource(
+        private val localBluetoothManager: LocalBluetoothManager?,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+        private val logger: BluetoothTileDialogLogger,
+    ) : LaunchSettingsCriteria {
+        // If currently broadcasting and the clicked device is not connected to the source
+        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+            return withContext(backgroundDispatcher) {
+                val matched =
+                    inAudioSharing &&
+                        deviceItem.isMediaDevice &&
+                        !BluetoothUtils.hasConnectedBroadcastSource(
+                            deviceItem.cachedBluetoothDevice,
+                            localBluetoothManager
+                        )
+
+                if (matched) {
+                    logger.logLaunchSettingsCriteriaMatched("InSharingClickedNoSource", deviceItem)
+                }
+
+                matched
+            }
+        }
+
+        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+            if (deviceItem.isLeAudioSupported)
+                BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED
+            else BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED
+    }
+
+    private class NotSharingClickedNonConnect(
+        private val leAudioProfile: LeAudioProfile?,
+        private val assistantProfile: LocalBluetoothLeBroadcastAssistant?,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+        private val logger: BluetoothTileDialogLogger,
+    ) : LaunchSettingsCriteria {
+        // If not broadcasting, having one device connected, and clicked on a not yet connected LE
+        // audio device
+        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+            return withContext(backgroundDispatcher) {
+                val matched =
+                    leAudioProfile?.let { leAudio ->
+                        assistantProfile?.let { assistant ->
+                            !inAudioSharing &&
+                                getCurrentConnectedLeByGroupId(
+                                        leAudio,
+                                        assistant,
+                                        backgroundDispatcher,
+                                        logger
+                                    )
+                                    .size == 1 &&
+                                deviceItem.isNotConnectedLeAudioSupported
+                        }
+                    } ?: false
+
+                if (matched) {
+                    logger.logLaunchSettingsCriteriaMatched(
+                        "NotSharingClickedNonConnect",
+                        deviceItem
+                    )
+                }
+
+                matched
+            }
+        }
+
+        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED
+    }
+
+    private class NotSharingClickedConnected(
+        private val leAudioProfile: LeAudioProfile?,
+        private val assistantProfile: LocalBluetoothLeBroadcastAssistant?,
+        @Background private val backgroundDispatcher: CoroutineDispatcher,
+        private val logger: BluetoothTileDialogLogger,
+    ) : LaunchSettingsCriteria {
+        // If not broadcasting, having two device connected, clicked on any connected LE audio
+        // devices
+        override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean {
+            return withContext(backgroundDispatcher) {
+                val matched =
+                    leAudioProfile?.let { leAudio ->
+                        assistantProfile?.let { assistant ->
+                            !inAudioSharing &&
+                                getCurrentConnectedLeByGroupId(
+                                        leAudio,
+                                        assistant,
+                                        backgroundDispatcher,
+                                        logger
+                                    )
+                                    .size == 2 &&
+                                deviceItem.isActiveOrConnectedLeAudioSupported
+                        }
+                    } ?: false
+
+                if (matched) {
+                    logger.logLaunchSettingsCriteriaMatched(
+                        "NotSharingClickedConnected",
+                        deviceItem
+                    )
+                }
+
+                matched
+            }
+        }
+
+        override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
+            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED
+    }
+
+    private companion object {
+        const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
+
+        val DeviceItem.isLeAudioSupported: Boolean
+            get() =
+                cachedBluetoothDevice.profiles.any { profile ->
+                    profile is LeAudioProfile && profile.isEnabled(cachedBluetoothDevice.device)
+                }
+
+        val DeviceItem.isNotConnectedLeAudioSupported: Boolean
+            get() = type == DeviceItemType.SAVED_BLUETOOTH_DEVICE && isLeAudioSupported
+
+        val DeviceItem.isActiveOrConnectedLeAudioSupported: Boolean
+            get() =
+                (type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE ||
+                    type == DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) && isLeAudioSupported
+
+        val DeviceItem.isMediaDevice: Boolean
+            get() =
+                cachedBluetoothDevice.connectableProfiles.any {
+                    it is A2dpProfile ||
+                        it is HearingAidProfile ||
+                        it is LeAudioProfile ||
+                        it is HeadsetProfile
+                }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index 8993a3b..38f51da 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -19,23 +19,25 @@
 import android.annotation.SuppressLint
 import android.app.DreamManager
 import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
 import com.android.systemui.Flags.communalHub
+import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
 import com.android.systemui.Flags.restartDreamOnUnocclude
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 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.filterState
 import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.sample
-import com.android.systemui.util.kotlin.sample
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
-import javax.inject.Inject
 
 /**
  * A [CoreStartable] responsible for automatically starting the dream when the communal hub is
@@ -48,7 +50,6 @@
     private val powerInteractor: PowerInteractor,
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-    private val communalInteractor: CommunalInteractor,
     private val dreamManager: DreamManager,
     @Background private val bgScope: CoroutineScope,
 ) : CoreStartable {
@@ -60,31 +61,28 @@
 
         // Return to dream from occluded when not already dreaming.
         if (restartDreamOnUnocclude()) {
-            keyguardTransitionInteractor.startedKeyguardTransitionStep
-                .sample(keyguardInteractor.isDreaming, ::Pair)
-                .filter {
-                    it.first.from == KeyguardState.OCCLUDED &&
-                        it.first.to == KeyguardState.DREAMING &&
-                        !it.second
-                }
+            keyguardTransitionInteractor
+                .transition(Edge.create(from = KeyguardState.OCCLUDED, to = KeyguardState.DREAMING))
+                .filterState(TransitionState.STARTED)
+                .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming }
                 .onEach { dreamManager.startDream() }
                 .launchIn(bgScope)
         }
 
         // Restart the dream underneath the hub in order to support the ability to swipe
         // away the hub to enter the dream.
-        keyguardTransitionInteractor.finishedKeyguardState
-            .sample(powerInteractor.isAwake, keyguardInteractor.isDreaming)
-            .onEach { (finishedState, isAwake, dreaming) ->
-                if (
-                    finishedState == KeyguardState.GLANCEABLE_HUB &&
-                        !dreaming &&
-                        !glanceableHubAllowKeyguardWhenDreaming() &&
-                        dreamManager.canStartDreaming(isAwake)
-                ) {
-                    dreamManager.startDream()
-                }
+        keyguardTransitionInteractor
+            .transition(
+                edge = Edge.create(to = Scenes.Communal),
+                edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GLANCEABLE_HUB)
+            )
+            .filterState(TransitionState.FINISHED)
+            .sampleFilter(powerInteractor.isAwake) { isAwake ->
+                dreamManager.canStartDreaming(isAwake)
             }
+            .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming }
+            .filter { !glanceableHubAllowKeyguardWhenDreaming() }
+            .onEach { dreamManager.startDream() }
             .launchIn(bgScope)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index d522c7e..88c3f9f6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -208,7 +208,10 @@
                 // doing any translation.
                 CommunalScenes.Communal
             }
-            to == KeyguardState.GONE -> CommunalScenes.Blank
+            // Transitioning to Blank scene when entering the edit mode will be handled separately
+            // with custom animations.
+            to == KeyguardState.GONE && !communalInteractor.editModeOpen.value ->
+                CommunalScenes.Blank
             !docked && !KeyguardState.deviceIsAwakeInState(to) -> {
                 // If the user taps the screen and wakes the device within this timeout, we don't
                 // want to dismiss the hub
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
index 260dcba..7a4006d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
@@ -28,6 +28,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -51,7 +52,7 @@
     fun changeScene(toScene: SceneKey, transitionKey: TransitionKey? = null)
 
     /** Immediately snaps to the desired scene. */
-    fun snapToScene(toScene: SceneKey)
+    fun snapToScene(toScene: SceneKey, delayMillis: Long = 0)
 
     /**
      * Updates the transition state of the hub [SceneTransitionLayout].
@@ -92,10 +93,11 @@
         }
     }
 
-    override fun snapToScene(toScene: SceneKey) {
+    override fun snapToScene(toScene: SceneKey, delayMillis: Long) {
         applicationScope.launch {
             // SceneTransitionLayout state updates must be triggered on the thread the STL was
             // created on.
+            delay(delayMillis)
             sceneDataSource.snapToScene(toScene)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index fdb40fb..00678a8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.communal.widgets.CommunalAppWidgetHost
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
 import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -46,6 +47,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 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.log.LogBuffer
 import com.android.systemui.log.core.Logger
@@ -164,7 +166,7 @@
     /** Whether to start dreaming when returning from occluded */
     val dreamFromOccluded: Flow<Boolean> =
         keyguardTransitionInteractor
-            .transitionStepsToState(KeyguardState.OCCLUDED)
+            .transition(Edge.create(to = KeyguardState.OCCLUDED))
             .map { it.from == KeyguardState.DREAMING }
             .stateIn(scope = applicationScope, SharingStarted.Eagerly, false)
 
@@ -306,6 +308,7 @@
         preselectedKey: String? = null,
         shouldOpenWidgetPickerOnStart: Boolean = false,
     ) {
+        communalSceneInteractor.setEditModeState(EditModeState.STARTING)
         editWidgetsActivityStarter.startActivity(preselectedKey, shouldOpenWidgetPickerOnStart)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 5cfe979..fd540c4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -22,14 +22,18 @@
 import com.android.systemui.communal.data.repository.CommunalSceneRepository
 import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
 import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
@@ -53,8 +57,18 @@
     }
 
     /** Immediately snaps to the new scene. */
-    fun snapToScene(newScene: SceneKey) {
-        communalSceneRepository.snapToScene(newScene)
+    fun snapToScene(newScene: SceneKey, delayMillis: Long = 0) {
+        communalSceneRepository.snapToScene(newScene, delayMillis)
+    }
+
+    /** Changes to Blank scene when starting an activity after dismissing keyguard. */
+    fun changeSceneForActivityStartOnDismissKeyguard() {
+        // skip if we're starting edit mode activity, as it will be handled later by changeScene
+        // with transition key [CommunalTransitionKeys.ToEditMode].
+        if (_editModeState.value == EditModeState.STARTING) {
+            return
+        }
+        changeScene(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
     }
 
     /**
@@ -62,6 +76,17 @@
      */
     val currentScene: Flow<SceneKey> = communalSceneRepository.currentScene
 
+    private val _editModeState = MutableStateFlow<EditModeState?>(null)
+    /**
+     * Current state for glanceable hub edit mode, used to chain the animations when transitioning
+     * between communal scene and the edit mode activity.
+     */
+    val editModeState = _editModeState.asStateFlow()
+
+    fun setEditModeState(value: EditModeState?) {
+        _editModeState.value = value
+    }
+
     /** Transition state of the hub mode. */
     val transitionState: StateFlow<ObservableTransitionState> =
         communalSceneRepository.transitionState
@@ -120,8 +145,14 @@
      *
      * This flow will be true during any transition and when idle on the communal scene.
      */
-    val isCommunalVisible: Flow<Boolean> =
-        transitionState.map {
-            !(it is ObservableTransitionState.Idle && it.currentScene == CommunalScenes.Blank)
-        }
+    val isCommunalVisible: StateFlow<Boolean> =
+        transitionState
+            .map {
+                !(it is ObservableTransitionState.Idle && it.currentScene == CommunalScenes.Blank)
+            }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
index 8b816db..4eaba06 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
@@ -21,4 +21,5 @@
     DEFAULT(0),
     STATIC_GRADIENT(1),
     ANIMATED(2),
+    NONE(3),
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
index 73cfb52..11fb233 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt
@@ -26,6 +26,10 @@
 object CommunalTransitionKeys {
     /** Fades the glanceable hub without any translation */
     val SimpleFade = TransitionKey("SimpleFade")
+    /** Transition from the glanceable hub before entering edit mode */
+    val ToEditMode = TransitionKey("ToEditMode")
+    /** Transition to the glanceable hub after exiting edit mode */
+    val FromEditMode = TransitionKey("FromEditMode")
     /** Immediately transitions without any delay */
     val Immediately = TransitionKey("Immediately")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt
new file mode 100644
index 0000000..ace9c2e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt
@@ -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.systemui.communal.shared.model
+
+/**
+ * Models the state of the edit mode activity. Used to chain the animation during the transition
+ * between the hub on communal scene, and the edit mode activity after unlocking the keyguard.
+ */
+enum class EditModeState(val value: Int) {
+    // starting activity after dismissing keyguard
+    STARTING(0),
+    // activity content is showing
+    SHOWING(1),
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
new file mode 100644
index 0000000..a88b777
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.smartspace
+
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.RemoteViews
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.communal.util.InteractionHandlerDelegate
+import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+
+/**
+ * Handles interactions on smartspace elements on the hub.
+ */
+class SmartspaceInteractionHandler @Inject constructor(
+    private val activityStarter: ActivityStarter,
+) : RemoteViews.InteractionHandler {
+    private val delegate = InteractionHandlerDelegate(
+        findViewToAnimate = { view -> view is SmartspaceAppWidgetHostView },
+        intentStarter = this::startIntent,
+    )
+
+    override fun onInteraction(
+        view: View,
+        pendingIntent: PendingIntent,
+        response: RemoteViews.RemoteResponse
+    ): Boolean = delegate.onInteraction(view, pendingIntent, response)
+
+    private fun startIntent(
+        pendingIntent: PendingIntent,
+        fillInIntent: Intent,
+        extraOptions: ActivityOptions,
+        animationController: ActivityTransitionAnimator.Controller?
+    ): Boolean {
+        activityStarter.startPendingIntentWithoutDismissing(
+            pendingIntent,
+            /* dismissShade = */ false,
+            /* intentSentUiThreadCallback = */ null,
+            animationController,
+            fillInIntent,
+            extraOptions.toBundle()
+        )
+        return true
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 8cd5603..cc90730 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.media.controls.ui.view.MediaHost
 import kotlinx.coroutines.flow.Flow
@@ -34,12 +35,15 @@
 
 /** The base view model for the communal hub. */
 abstract class BaseCommunalViewModel(
-    private val communalSceneInteractor: CommunalSceneInteractor,
+    val communalSceneInteractor: CommunalSceneInteractor,
     private val communalInteractor: CommunalInteractor,
     val mediaHost: MediaHost,
 ) {
     val currentScene: Flow<SceneKey> = communalSceneInteractor.currentScene
 
+    /** Used to animate showing or hiding the communal content. */
+    open val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(false)
+
     /** Whether communal hub should be focused by accessibility tools. */
     open val isFocusable: Flow<Boolean> = MutableStateFlow(false)
 
@@ -63,6 +67,8 @@
         communalSceneInteractor.changeScene(scene, transitionKey)
     }
 
+    fun setEditModeState(state: EditModeState?) = communalSceneInteractor.setEditModeState(state)
+
     /**
      * Updates the transition state of the hub [SceneTransitionLayout].
      *
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index bc65ccb..c0c5861 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -24,25 +24,33 @@
 import android.util.Log
 import androidx.activity.result.ActivityResultLauncher
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags.enableWidgetPickerSizeFilter
 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.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.log.CommunalUiEvent
+import com.android.systemui.communal.shared.model.EditModeState
 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
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.res.R
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import javax.inject.Inject
 import javax.inject.Named
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.withContext
 
@@ -54,6 +62,7 @@
     communalSceneInteractor: CommunalSceneInteractor,
     private val communalInteractor: CommunalInteractor,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
     private val uiEventLogger: UiEventLogger,
     @CommunalLog logBuffer: LogBuffer,
@@ -64,6 +73,20 @@
 
     override val isEditMode = true
 
+    override val isCommunalContentVisible: Flow<Boolean> =
+        communalSceneInteractor.editModeState.map { it == EditModeState.SHOWING }
+
+    /**
+     * Emits when edit mode activity can show, after we've transitioned to [KeyguardState.GONE]
+     * and edit mode is open.
+     */
+    val canShowEditMode =
+        allOf(
+                keyguardTransitionInteractor.isFinishedInState(KeyguardState.GONE),
+                communalInteractor.editModeOpen
+            )
+            .filter { it }
+
     // Only widgets are editable.
     override val communalContent: Flow<List<CommunalContentModel>> =
         communalInteractor.widgetContent.onEach { models ->
@@ -138,6 +161,16 @@
 
         return Intent(Intent.ACTION_PICK).apply {
             setPackage(packageName)
+            if (enableWidgetPickerSizeFilter()) {
+                putExtra(
+                    EXTRA_DESIRED_WIDGET_WIDTH,
+                    resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
+                )
+                putExtra(
+                    EXTRA_DESIRED_WIDGET_HEIGHT,
+                    resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height)
+                )
+            }
             putExtra(
                 AppWidgetManager.EXTRA_CATEGORY_FILTER,
                 communalSettingsInteractor.communalWidgetCategories.value
@@ -160,9 +193,16 @@
     /** Sets whether edit mode is currently open */
     fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen)
 
+    /** Called when exiting the edit mode, before transitioning back to the communal scene. */
+    fun cleanupEditModeState() {
+        communalSceneInteractor.setEditModeState(null)
+    }
+
     companion object {
         private const val TAG = "CommunalEditModeViewModel"
 
+        private const val EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width"
+        private const val EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height"
         private const val EXTRA_UI_SURFACE_KEY = "ui_surface"
         private const val EXTRA_UI_SURFACE_VALUE = "widgets_hub"
         const val EXTRA_ADDED_APP_WIDGETS_KEY = "added_app_widgets"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index 9114aab..e1408a0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -18,23 +18,32 @@
 
 import android.graphics.Color
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.util.CommunalColors
 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
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
 
 /** View model for transitions related to the communal hub. */
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -42,28 +51,34 @@
 class CommunalTransitionViewModel
 @Inject
 constructor(
+    @Application applicationScope: CoroutineScope,
     communalColors: CommunalColors,
     glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel,
     lockscreenToGlanceableHubTransitionViewModel: LockscreenToGlanceableHubTransitionViewModel,
     dreamToGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel,
     glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel,
     communalInteractor: CommunalInteractor,
-    keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    communalSceneInteractor: CommunalSceneInteractor,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor
 ) {
     // Show UMO on glanceable hub immediately on transition into glanceable hub
     private val showUmoFromOccludedToGlanceableHub: Flow<Boolean> =
         keyguardTransitionInteractor
-            .transitionStepsFromState(KeyguardState.OCCLUDED)
+            .transition(
+                Edge.create(from = KeyguardState.OCCLUDED, to = KeyguardState.GLANCEABLE_HUB)
+            )
             .filter {
-                it.to == KeyguardState.GLANCEABLE_HUB &&
-                    (it.transitionState == TransitionState.STARTED ||
-                        it.transitionState == TransitionState.CANCELED)
+                (it.transitionState == TransitionState.STARTED ||
+                    it.transitionState == TransitionState.CANCELED)
             }
             .map { it.transitionState == TransitionState.STARTED }
 
     private val showUmoFromGlanceableHubToOccluded: Flow<Boolean> =
         keyguardTransitionInteractor
-            .transitionStepsFromState(KeyguardState.GLANCEABLE_HUB)
+            .transition(
+                edge = Edge.create(from = Scenes.Communal),
+                edgeWithoutSceneContainer = Edge.create(from = KeyguardState.GLANCEABLE_HUB)
+            )
             .filter {
                 it.to == KeyguardState.OCCLUDED &&
                     (it.transitionState == TransitionState.FINISHED ||
@@ -77,25 +92,43 @@
      * of UMO should be updated.
      */
     val isUmoOnCommunal: Flow<Boolean> =
-        merge(
-                lockscreenToGlanceableHubTransitionViewModel.showUmo,
-                glanceableHubToLockscreenTransitionViewModel.showUmo,
-                dreamToGlanceableHubTransitionViewModel.showUmo,
-                glanceableHubToDreamTransitionViewModel.showUmo,
-                showUmoFromOccludedToGlanceableHub,
-                showUmoFromGlanceableHubToOccluded,
+        anyOf(
+                communalSceneInteractor.isIdleOnCommunal,
+                allOf(
+                    // Only show UMO on the hub if the hub is at least partially visible. This
+                    // prevents
+                    // the UMO from being missing on the lock screen when going from the hub to lock
+                    // screen in some way other than through a direct transition, such as unlocking
+                    // from
+                    // the hub, then pressing power twice to go back to the lock screen.
+                    communalSceneInteractor.isCommunalVisible,
+                    merge(
+                            lockscreenToGlanceableHubTransitionViewModel.showUmo,
+                            glanceableHubToLockscreenTransitionViewModel.showUmo,
+                            dreamToGlanceableHubTransitionViewModel.showUmo,
+                            glanceableHubToDreamTransitionViewModel.showUmo,
+                            showUmoFromOccludedToGlanceableHub,
+                            showUmoFromGlanceableHubToOccluded,
+                        )
+                        .onStart { emit(false) }
+                )
             )
-            .distinctUntilChanged()
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false
+            )
 
     /** Whether to show communal when exiting the occluded state. */
     val showCommunalFromOccluded: Flow<Boolean> = communalInteractor.showCommunalFromOccluded
 
     val transitionFromOccludedEnded =
-        keyguardTransitionInteractor.transitionStepsFromState(KeyguardState.OCCLUDED).filter { step
-            ->
-            step.transitionState == TransitionState.FINISHED ||
-                step.transitionState == TransitionState.CANCELED
-        }
+        keyguardTransitionInteractor
+            .transition(Edge.create(from = KeyguardState.OCCLUDED))
+            .filter { step ->
+                step.transitionState == TransitionState.FINISHED ||
+                    step.transitionState == TransitionState.CANCELED
+            }
 
     val recentsBackgroundColor: Flow<Color?> =
         combine(showCommunalFromOccluded, communalColors.backgroundColor) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index c6fa5a84..11247df 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -125,6 +125,8 @@
                 logger.d({ "Content updated: $str1" }) { str1 = models.joinToString { it.key } }
             }
 
+    override val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(true)
+
     /**
      * Freeze the content flow, when an activity is about to show, like starting a timer via voice:
      * 1) in handheld mode, use the keyguard occluded state;
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
new file mode 100644
index 0000000..40b182d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.util
+
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.RemoteViews
+import androidx.core.util.component1
+import androidx.core.util.component2
+import com.android.systemui.animation.ActivityTransitionAnimator
+
+
+/** A delegate that can be used to launch activities from [RemoteViews] */
+class InteractionHandlerDelegate(
+    private val findViewToAnimate: (View) -> Boolean,
+    private val intentStarter: IntentStarter,
+) : RemoteViews.InteractionHandler {
+
+    /**
+     * Responsible for starting the pending intent for launching activities.
+     */
+    fun interface IntentStarter {
+        fun startPendingIntent(
+            intent: PendingIntent,
+            fillInIntent: Intent,
+            activityOptions: ActivityOptions,
+            controller: ActivityTransitionAnimator.Controller?,
+        ): Boolean
+    }
+
+    override fun onInteraction(
+        view: View,
+        pendingIntent: PendingIntent,
+        response: RemoteViews.RemoteResponse
+    ): Boolean {
+        val launchOptions = response.getLaunchOptions(view)
+        return when {
+            pendingIntent.isActivity -> {
+                // Forward the fill-in intent and activity options retrieved from the response
+                // to populate the pending intent, so that list items can launch respective
+                // activities.
+                val hostView = getNearestParent(view)
+                val animationController =
+                    hostView?.let(ActivityTransitionAnimator.Controller::fromView)
+                val (fillInIntent, activityOptions) = launchOptions
+                intentStarter.startPendingIntent(
+                    pendingIntent,
+                    fillInIntent,
+                    activityOptions,
+                    animationController
+                )
+            }
+
+            else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
+        }
+    }
+
+    private fun getNearestParent(child: View): View? {
+        var view: Any? = child
+        while (view is View) {
+            if (findViewToAnimate(view)) return view
+            view = view.parent
+        }
+        return null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 426f484..40df6cec 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.communal.ui.compose.CommunalHub
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
@@ -70,8 +71,6 @@
 
     private var shouldOpenWidgetPickerOnStart = false
 
-    private var lockOnDestroy = false
-
     private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
         registerForActivityResult(StartActivityForResult()) { result ->
             when (result.resultCode) {
@@ -97,8 +96,7 @@
                                 run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
                             }
                         }
-                    }
-                        ?: run { Log.w(TAG, "No data in result.") }
+                    } ?: run { Log.w(TAG, "No data in result.") }
                 }
                 else ->
                     Log.w(
@@ -110,6 +108,7 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+        listenForTransitionAndChangeScene()
 
         communalViewModel.setEditModeOpen(true)
 
@@ -141,6 +140,22 @@
         }
     }
 
+    // Handle scene change to show the activity and animate in its content
+    private fun listenForTransitionAndChangeScene() {
+        lifecycleScope.launch {
+            communalViewModel.canShowEditMode.collect {
+                communalViewModel.changeScene(
+                    CommunalScenes.Blank,
+                    CommunalTransitionKeys.ToEditMode
+                )
+                // wait till transitioned to Blank scene, then animate in communal content in
+                // edit mode
+                communalViewModel.currentScene.first { it == CommunalScenes.Blank }
+                communalViewModel.setEditModeState(EditModeState.SHOWING)
+            }
+        }
+    }
+
     private fun onOpenWidgetPicker() {
         lifecycleScope.launch {
             communalViewModel.onOpenWidgetPicker(
@@ -153,16 +168,18 @@
 
     private fun onEditDone() {
         lifecycleScope.launch {
+            communalViewModel.cleanupEditModeState()
+
             communalViewModel.changeScene(
                 CommunalScenes.Communal,
-                CommunalTransitionKeys.SimpleFade
+                CommunalTransitionKeys.FromEditMode
             )
 
             // Wait for the current scene to be idle on communal.
             communalViewModel.isIdleOnCommunal.first { it }
-            // Then finish the activity (this helps to avoid a flash of lockscreen when locking
-            // in onDestroy()).
-            lockOnDestroy = true
+
+            // Lock to go back to the hub after exiting.
+            lockNow()
             finish()
         }
     }
@@ -195,9 +212,8 @@
 
     override fun onDestroy() {
         super.onDestroy()
+        communalViewModel.cleanupEditModeState()
         communalViewModel.setEditModeOpen(false)
-
-        if (lockOnDestroy) lockNow()
     }
 
     private fun lockNow() {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.kt
new file mode 100644
index 0000000..7f11463
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/SmartspaceAppWidgetHostView.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.communal.widgets
+
+import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetProviderInfo
+import android.content.Context
+import com.android.systemui.animation.LaunchableView
+import com.android.systemui.animation.LaunchableViewDelegate
+
+/** AppWidgetHostView that displays in communal hub to show smartspace content. */
+class SmartspaceAppWidgetHostView(context: Context) : AppWidgetHostView(context), LaunchableView {
+    private val launchableViewDelegate =
+        LaunchableViewDelegate(
+            this,
+            superSetVisibility = { super.setVisibility(it) },
+        )
+
+    override fun setAppWidget(appWidgetId: Int, info: AppWidgetProviderInfo?) {
+        super.setAppWidget(appWidgetId, info)
+        setPadding(0, 0, 0, 0)
+    }
+
+    override fun getRemoteContextEnsuringCorrectCachedApkPath(): Context? {
+        // Silence errors
+        return null
+    }
+
+    override fun setShouldBlockVisibilityChanges(block: Boolean) =
+        launchableViewDelegate.setShouldBlockVisibilityChanges(block)
+
+    override fun setVisibility(visibility: Int) = launchableViewDelegate.setVisibility(visibility)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index 51a3a6d..cbc6c97 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -19,53 +19,46 @@
 import android.app.ActivityOptions
 import android.app.PendingIntent
 import android.content.Intent
-import android.util.Pair
 import android.view.View
 import android.widget.RemoteViews
-import androidx.core.util.component1
-import androidx.core.util.component2
 import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.common.ui.view.getNearestParent
+import com.android.systemui.communal.util.InteractionHandlerDelegate
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.ActivityStarter
 import javax.inject.Inject
 
+@SysUISingleton
 class WidgetInteractionHandler
 @Inject
 constructor(
     private val activityStarter: ActivityStarter,
 ) : RemoteViews.InteractionHandler {
+
+    private val delegate = InteractionHandlerDelegate(
+        findViewToAnimate = { view -> view is CommunalAppWidgetHostView },
+        intentStarter = this::startIntent,
+    )
+
     override fun onInteraction(
         view: View,
         pendingIntent: PendingIntent,
         response: RemoteViews.RemoteResponse
-    ): Boolean {
-        val launchOptions = response.getLaunchOptions(view)
-        return when {
-            pendingIntent.isActivity ->
-                // Forward the fill-in intent and activity options retrieved from the response
-                // to populate the pending intent, so that list items can launch respective
-                // activities.
-                startActivity(view, pendingIntent, launchOptions)
-            else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
-        }
-    }
+    ): Boolean = delegate.onInteraction(view, pendingIntent, response)
 
-    private fun startActivity(
-        view: View,
+
+    private fun startIntent(
         pendingIntent: PendingIntent,
-        launchOptions: Pair<Intent, ActivityOptions>,
+        fillInIntent: Intent,
+        extraOptions: ActivityOptions,
+        controller: ActivityTransitionAnimator.Controller?
     ): Boolean {
-        val hostView = view.getNearestParent<CommunalAppWidgetHostView>()
-        val animationController = hostView?.let(ActivityTransitionAnimator.Controller::fromView)
-        val (fillInIntent, activityOptions) = launchOptions
-
         activityStarter.startPendingIntentMaybeDismissingKeyguard(
             pendingIntent,
             /* dismissShade = */ false,
             /* intentSentUiThreadCallback = */ null,
-            animationController,
+            controller,
             fillInIntent,
-            activityOptions.toBundle(),
+            extraOptions.toBundle(),
         )
         return true
     }
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index e284bc7..92108e9 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -36,7 +36,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.Utils;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
 import com.android.systemui.controls.ControlsServiceInfo;
@@ -137,9 +136,7 @@
 
         private void updateHomeControlsComplication() {
             mControlsComponent.getControlsListingController().ifPresent(c -> {
-                final boolean replacedWithOpenHub =
-                        Flags.glanceableHubShortcutButton() && mReplacedByOpenHub;
-                if (isHomeControlsAvailable(c.getCurrentServices()) && !replacedWithOpenHub) {
+                if (isHomeControlsAvailable(c.getCurrentServices())) {
                     mDreamOverlayStateController.addComplication(mComplication);
                 } else {
                     mDreamOverlayStateController.removeComplication(mComplication);
diff --git a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
index a679bfb..05df2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
@@ -28,7 +28,6 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.CoreStartable;
-import com.android.systemui.Flags;
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.communal.shared.model.CommunalScenes;
 import com.android.systemui.complication.dagger.OpenHubComplicationComponent;
@@ -111,11 +110,11 @@
 
         private void updateOpenHubComplication() {
             // TODO(b/339667383): don't show the complication if glanceable hub is disabled
-            if (Flags.glanceableHubShortcutButton()) {
-                mDreamOverlayStateController.addComplication(mComplication);
-            } else {
-                mDreamOverlayStateController.removeComplication(mComplication);
-            }
+//            if (Flags.glanceableHubShortcutButton()) {
+//                mDreamOverlayStateController.addComplication(mComplication);
+//            } else {
+//                mDreamOverlayStateController.removeComplication(mComplication);
+//            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.kt
new file mode 100644
index 0000000..a91ce16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/CommonSystemUIUnfoldModule.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.dagger
+
+import com.android.systemui.unfold.SysUIUnfoldComponent
+import com.android.systemui.unfold.SysUIUnfoldModule.BoundFromSysUiUnfoldModule
+import dagger.BindsOptionalOf
+import dagger.Module
+import dagger.Provides
+import java.util.Optional
+import kotlin.jvm.optionals.getOrElse
+
+
+/**
+ * Module for foldable-related classes that is available in all SystemUI variants.
+ * Provides `Optional<SysUIUnfoldComponent>` which is present when the device is a foldable
+ * device that has fold/unfold animation enabled.
+ */
+@Module
+abstract class CommonSystemUIUnfoldModule {
+
+    /* Note this will be injected as @BoundFromSysUiUnfoldModule Optional<Optional<...>> */
+    @BindsOptionalOf
+    @BoundFromSysUiUnfoldModule
+    abstract fun optionalSysUiUnfoldComponent(): Optional<SysUIUnfoldComponent>
+
+    companion object {
+        @Provides
+        @SysUISingleton
+        fun sysUiUnfoldComponent(
+            /**
+             * This will be empty when [com.android.systemui.unfold.SysUIUnfoldModule] is not part
+             * of the graph, and contain the optional when it is.
+             */
+            @BoundFromSysUiUnfoldModule
+            optionalOfOptional: Optional<Optional<SysUIUnfoldComponent>>
+        ): Optional<SysUIUnfoldComponent> = optionalOfOptional.getOrElse { Optional.empty() }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index c2e1e33..2fbb75e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -29,6 +29,7 @@
 import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
 import com.android.systemui.settings.brightness.BrightnessDialog;
 import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity;
+import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity;
 import com.android.systemui.tuner.TunerActivity;
 import com.android.systemui.usb.UsbAccessoryUriActivity;
 import com.android.systemui.usb.UsbConfirmActivity;
@@ -156,4 +157,10 @@
     @ClassKey(SwitchToManagedProfileForCallActivity.class)
     public abstract Activity bindSwitchToManagedProfileForCallActivity(
             SwitchToManagedProfileForCallActivity activity);
+
+    /** Inject into TouchpadTutorialActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(TouchpadTutorialActivity.class)
+    public abstract Activity bindTouchpadTutorialActivity(TouchpadTutorialActivity activity);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
index a431a59..b71af69 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java
@@ -19,6 +19,7 @@
 import com.android.systemui.keyguard.CustomizationProvider;
 import com.android.systemui.statusbar.NotificationInsetsModule;
 import com.android.systemui.statusbar.QsFrameTranslateModule;
+import com.android.systemui.unfold.SysUIUnfoldModule;
 
 import dagger.Subcomponent;
 
@@ -34,6 +35,7 @@
         SystemUIBinder.class,
         SystemUIModule.class,
         SystemUICoreStartableModule.class,
+        SysUIUnfoldModule.class,
         ReferenceSystemUIModule.class})
 public interface ReferenceSysUIComponent extends SysUIComponent {
 
@@ -51,3 +53,4 @@
      */
     void inject(CustomizationProvider customizationProvider);
 }
+
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 7aab37e..9f0fc51 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -31,6 +31,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
+import com.android.systemui.keyboard.shortcut.ShortcutHelperModule;
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
 import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
 import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
@@ -137,7 +138,8 @@
         UnfoldTransitionModule.Startables.class,
         ToastModule.class,
         VolumeModule.class,
-        WallpaperModule.class
+        WallpaperModule.class,
+        ShortcutHelperModule.class,
 })
 public abstract class ReferenceSystemUIModule {
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 2ebb94f..a7ff3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -143,7 +143,6 @@
 import com.android.systemui.telephony.data.repository.TelephonyRepositoryModule;
 import com.android.systemui.temporarydisplay.dagger.TemporaryDisplayModule;
 import com.android.systemui.tuner.dagger.TunerModule;
-import com.android.systemui.unfold.SysUIUnfoldModule;
 import com.android.systemui.user.UserModule;
 import com.android.systemui.user.domain.UserDomainLayerModule;
 import com.android.systemui.util.EventLogModule;
@@ -254,7 +253,7 @@
         SystemPropertiesFlagsModule.class,
         SysUIConcurrencyModule.class,
         SysUICoroutinesModule.class,
-        SysUIUnfoldModule.class,
+        CommonSystemUIUnfoldModule.class,
         TelephonyRepositoryModule.class,
         TemporaryDisplayModule.class,
         TunerModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepository.kt
new file mode 100644
index 0000000..250b432
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepository.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.deviceconfig.data.repository
+
+import android.provider.DeviceConfig
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
+
+@SysUISingleton
+class DeviceConfigRepository
+@Inject
+constructor(
+    @Background private val backgroundExecutor: Executor,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val dataSource: DeviceConfigProxy,
+) {
+
+    fun property(namespace: String, name: String, default: Boolean): Flow<Boolean> {
+        return conflatedCallbackFlow {
+                val listener = { properties: DeviceConfig.Properties ->
+                    if (properties.keyset.contains(name)) {
+                        trySend(properties.getBoolean(name, default))
+                    }
+                }
+
+                dataSource.addOnPropertiesChangedListener(
+                    namespace,
+                    backgroundExecutor,
+                    listener,
+                )
+                trySend(dataSource.getBoolean(namespace, name, default))
+
+                awaitClose { dataSource.removeOnPropertiesChangedListener(listener) }
+            }
+            .flowOn(backgroundDispatcher)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractor.kt
new file mode 100644
index 0000000..d04f8bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractor.kt
@@ -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 com.android.systemui.deviceconfig.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceconfig.data.repository.DeviceConfigRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class DeviceConfigInteractor
+@Inject
+constructor(
+    private val repository: DeviceConfigRepository,
+) {
+
+    fun property(namespace: String, name: String, default: Boolean): Flow<Boolean> {
+        return repository.property(namespace, name, default)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 9d110e6..10d6881 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.util.kotlin.Quad
+import com.android.systemui.utils.coroutines.flow.mapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,6 +38,7 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
@@ -96,7 +98,16 @@
             .filter { currentScene ->
                 currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen
             }
-            .map { it == Scenes.Gone }
+            .mapLatestConflated { scene ->
+                if (scene == Scenes.Gone) {
+                    // Make sure device unlock status is definitely unlocked before we consider the
+                    // device "entered".
+                    deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked }
+                    true
+                } else {
+                    false
+                }
+            }
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
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 4ac0c56..9d6c2bf 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.display.data.repository
 
+import android.annotation.SuppressLint
 import android.hardware.display.DisplayManager
 import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED
 import android.hardware.display.DisplayManager.DisplayListener
@@ -27,6 +28,7 @@
 import android.view.Display
 import com.android.app.tracing.FlowTracing.traceEach
 import com.android.app.tracing.traceSection
+import com.android.systemui.Flags
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -42,11 +44,12 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.scan
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 
@@ -91,6 +94,7 @@
 }
 
 @SysUISingleton
+@SuppressLint("SharedFlowCreation")
 class DisplayRepositoryImpl
 @Inject
 constructor(
@@ -132,8 +136,50 @@
         allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId }
 
     override val displayAdditionEvent: Flow<Display?> =
-        allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map {
-            displayManager.getDisplay(it.displayId)
+        allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { getDisplay(it.displayId) }
+
+    private val oldEnabledDisplays: Flow<Set<Display>> =
+        allDisplayEvents
+            .map { getDisplays() }
+            .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1)
+
+    /** Propagate to the listeners only enabled displays */
+    private val enabledDisplayIds: Flow<Set<Int>> =
+        if (Flags.enableEfficientDisplayRepository()) {
+                allDisplayEvents
+                    .scan(initial = emptySet()) { previousIds: Set<Int>, event: DisplayEvent ->
+                        val id = event.displayId
+                        when (event) {
+                            is DisplayEvent.Removed -> previousIds - id
+                            is DisplayEvent.Added,
+                            is DisplayEvent.Changed -> previousIds + id
+                        }
+                    }
+                    .shareIn(
+                        bgApplicationScope,
+                        started = SharingStarted.WhileSubscribed(),
+                        replay = 1
+                    )
+            } else {
+                oldEnabledDisplays.map { enabledDisplaysSet ->
+                    enabledDisplaysSet.map { it.displayId }.toSet()
+                }
+            }
+            .debugLog("enabledDisplayIds")
+
+    /**
+     * Represents displays that went though the [DisplayListener.onDisplayAdded] callback.
+     *
+     * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
+     */
+    private val enabledDisplays: Flow<Set<Display>> =
+        if (Flags.enableEfficientDisplayRepository()) {
+            enabledDisplayIds
+                .mapElementsLazily { displayId -> getDisplay(displayId) }
+                .flowOn(backgroundCoroutineDispatcher)
+                .debugLog("enabledDisplayIds")
+        } else {
+            oldEnabledDisplays
         }
 
     /**
@@ -141,35 +187,26 @@
      *
      * Those are commonly the ones provided by [DisplayManager.getDisplays] by default.
      */
-    private val enabledDisplays =
-        allDisplayEvents
-            .map { getDisplays() }
-            .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1)
-
     override val displays: Flow<Set<Display>> = enabledDisplays
 
     private fun getDisplays(): Set<Display> =
         traceSection("$TAG#getDisplays()") { displayManager.displays?.toSet() ?: emptySet() }
 
-    /** Propagate to the listeners only enabled displays */
-    private val enabledDisplayIds: Flow<Set<Int>> =
-        enabledDisplays
-            .map { enabledDisplaysSet -> enabledDisplaysSet.map { it.displayId }.toSet() }
-            .debugLog("enabledDisplayIds")
-
     private val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet())
     private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds")
 
     private fun getInitialConnectedDisplays(): Set<Int> =
-        displayManager
-            .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
-            .map { it.displayId }
-            .toSet()
-            .also {
-                if (DEBUG) {
-                    Log.d(TAG, "getInitialConnectedDisplays: $it")
+        traceSection("$TAG#getInitialConnectedDisplays") {
+            displayManager
+                .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
+                .map { it.displayId }
+                .toSet()
+                .also {
+                    if (DEBUG) {
+                        Log.d(TAG, "getInitialConnectedDisplays: $it")
+                    }
                 }
-            }
+        }
 
     /* keeps connected displays until they are disconnected. */
     private val connectedDisplayIds: StateFlow<Set<Int>> =
@@ -230,6 +267,9 @@
     private fun getDisplayType(displayId: Int): Int? =
         traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type }
 
+    private fun getDisplay(displayId: Int): Display? =
+        traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) }
+
     /**
      * Pending displays are the ones connected, but not enabled and not ignored.
      *
@@ -307,6 +347,30 @@
         }
     }
 
+    /**
+     * 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.
+     */
+    private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
+        var initialSet = emptySet<T>()
+        val currentMap = mutableMapOf<T, V>()
+        var resultSet = emptySet<V>()
+        return onEach { currentSet ->
+                val removed = initialSet - currentSet
+                val added = currentSet - initialSet
+                if (added.isNotEmpty() || removed.isNotEmpty()) {
+                    added.forEach { key: T -> createValue(key)?.let { currentMap[key] = it } }
+                    removed.forEach { key: T -> currentMap.remove(key) }
+                    resultSet = currentMap.values.toSet() // Creates a **copy** of values
+                }
+                initialSet = currentSet
+            }
+            .map { resultSet }
+    }
+
     private companion object {
         const val TAG = "DisplayRepository"
         val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 9311187..4a9f741 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -40,6 +40,7 @@
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Flags;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dock.DockManager;
@@ -564,6 +565,12 @@
             return;
         }
 
+        // When already in pulsing, we can show the new Notification without requesting a new pulse.
+        if (Flags.notificationPulsingFix()
+                && dozeState == State.DOZE_PULSING && reason == DozeLog.PULSE_REASON_NOTIFICATION) {
+            return;
+        }
+
         if (!mAllowPulseTriggers || mDozeHost.isPulsePending()
                 || !canPulse(dozeState, performedProxCheck)) {
             if (!mAllowPulseTriggers) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 95012a2..1a06418 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.doze.DozeMachine.State.DOZE;
 import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.Flags.dozeuiSchedulingAlarmsBackgroundExecution;
 
 import android.app.AlarmManager;
 import android.content.Context;
@@ -70,6 +71,7 @@
     @Inject
     public DozeUi(Context context, AlarmManager alarmManager,
             WakeLock wakeLock, DozeHost host, @Main Handler handler,
+            @Background Handler bgHandler,
             DozeParameters params,
             @Background DelayableExecutor bgExecutor,
             DozeLog dozeLog) {
@@ -80,7 +82,13 @@
         mBgExecutor = bgExecutor;
         mCanAnimateTransition = !params.getDisplayNeedsBlanking();
         mDozeParameters = params;
-        mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler);
+        if (dozeuiSchedulingAlarmsBackgroundExecution()) {
+            mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick",
+                    bgHandler);
+        } else {
+            mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick",
+                    handler);
+        }
         mDozeLog = dozeLog;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index f860893..3294c81 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -27,11 +27,13 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
 import com.android.dream.lowlight.util.TruncatedInterpolator
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController
 import com.android.systemui.complication.ComplicationHostViewController
 import com.android.systemui.complication.ComplicationLayoutParams
 import com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM
 import com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP
 import com.android.systemui.complication.ComplicationLayoutParams.Position
+import com.android.systemui.dreams.dagger.DreamOverlayComponent.DreamOverlayScope
 import com.android.systemui.dreams.dagger.DreamOverlayModule
 import com.android.systemui.dreams.ui.viewmodel.DreamViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -45,12 +47,13 @@
 import kotlinx.coroutines.launch
 
 /** Controller for dream overlay animations. */
+@DreamOverlayScope
 class DreamOverlayAnimationsController
 @Inject
 constructor(
     private val mBlurUtils: BlurUtils,
     private val mComplicationHostViewController: ComplicationHostViewController,
-    private val mStatusBarViewController: DreamOverlayStatusBarViewController,
+    private val mStatusBarViewController: AmbientStatusBarViewController,
     private val mOverlayStateController: DreamOverlayStateController,
     @Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int,
     private val dreamViewModel: DreamViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 1e725eb..245def8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -40,6 +40,7 @@
 
 import com.android.app.animation.Interpolators;
 import com.android.dream.lowlight.LowLightTransitionCoordinator;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
 import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
@@ -55,6 +56,7 @@
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.BlurUtils;
+import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.ViewController;
 
 import kotlinx.coroutines.CoroutineDispatcher;
@@ -72,10 +74,12 @@
 public class DreamOverlayContainerViewController extends
         ViewController<DreamOverlayContainerView> implements
         LowLightTransitionCoordinator.LowLightEnterListener {
-    private final DreamOverlayStatusBarViewController mStatusBarViewController;
+    private final AmbientStatusBarViewController mStatusBarViewController;
+    private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
     private final BlurUtils mBlurUtils;
     private final DreamOverlayAnimationsController mDreamOverlayAnimationsController;
     private final DreamOverlayStateController mStateController;
+
     private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final ShadeInteractor mShadeInteractor;
@@ -188,8 +192,9 @@
             ComplicationHostViewController complicationHostViewController,
             @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
             @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView,
-            DreamOverlayStatusBarViewController statusBarViewController,
+            AmbientStatusBarViewController statusBarViewController,
             LowLightTransitionCoordinator lowLightTransitionCoordinator,
+            TouchInsetManager.TouchInsetSession touchInsetSession,
             BlurUtils blurUtils,
             @Main Handler handler,
             @Background CoroutineDispatcher backgroundDispatcher,
@@ -209,6 +214,7 @@
         super(containerView);
         mDreamOverlayContentView = contentView;
         mStatusBarViewController = statusBarViewController;
+        mTouchInsetSession = touchInsetSession;
         mBlurUtils = blurUtils;
         mDreamOverlayAnimationsController = animationsController;
         mStateController = stateController;
@@ -294,6 +300,7 @@
         mHandler.removeCallbacksAndMessages(null);
         mPrimaryBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
         mBouncerlessScrimController.removeCallback(mBouncerlessExpansionCallback);
+        mTouchInsetSession.clear();
 
         mDreamOverlayAnimationsController.cancelAnimations();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
index 409b196..0833518 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayDotImageView.java
@@ -31,12 +31,13 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.AlphaOptimizedImageView;
 
 /**
  * An {@link AlphaOptimizedImageView} that is responsible for rendering a dot. Used by
- * {@link DreamOverlayStatusBarView}.
+ * {@link AmbientStatusBarView}.
  */
 public class DreamOverlayDotImageView extends AlphaOptimizedImageView {
     private final @ColorInt int mDotColor;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 789b7f8..76fcabd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -25,9 +25,11 @@
 import androidx.lifecycle.LifecycleOwner;
 
 import com.android.internal.util.Preconditions;
+import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView;
+import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayContainerView;
-import com.android.systemui.dreams.DreamOverlayStatusBarView;
 import com.android.systemui.res.R;
 import com.android.systemui.touch.TouchInsetManager;
 
@@ -60,7 +62,7 @@
     public static DreamOverlayContainerView providesDreamOverlayContainerView(
             LayoutInflater layoutInflater) {
         return Preconditions.checkNotNull((DreamOverlayContainerView)
-                layoutInflater.inflate(R.layout.dream_overlay_container, null),
+                        layoutInflater.inflate(R.layout.dream_overlay_container, null),
                 "R.layout.dream_layout_container could not be properly inflated");
     }
 
@@ -95,13 +97,23 @@
     /** */
     @Provides
     @DreamOverlayComponent.DreamOverlayScope
-    public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
+    public static AmbientStatusBarView providesDreamOverlayStatusBarView(
             DreamOverlayContainerView view) {
         return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
                 "R.id.status_bar must not be null");
     }
 
-    /** */
+    /**
+     * Provides the view controller for the {@link AmbientStatusBarView}
+     */
+    @Provides
+    @DreamOverlayComponent.DreamOverlayScope
+    public static AmbientStatusBarViewController providesStatusBarViewController(
+            AmbientStatusBarView view, AmbientStatusBarComponent.Factory factory) {
+        return factory.create(view).getController();
+    }
+
+    /**  */
     @Provides
     @DreamOverlayComponent.DreamOverlayScope
     @Named(MAX_BURN_IN_OFFSET)
diff --git a/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
index dccb24d..e65a42d 100644
--- a/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
+++ b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
@@ -36,5 +36,17 @@
     public static final String ACTION_LAUNCH_EMERGENCY =
             "com.android.systemui.action.LAUNCH_EMERGENCY";
 
+    /**
+     * An alternate intent to launch the emergency flow if the device is in retail mode
+     */
+    public static final String ACTION_LAUNCH_EMERGENCY_RETAIL =
+            "com.android.systemui.action.LAUNCH_EMERGENCY_RETAIL";
+
+    /**
+     * Launches the emergency information screen
+     */
+    public static final String ACTION_LAUNCH_EMERGENCY_INFO =
+            "com.android.systemui.action.LAUNCH_EMERGENCY_INFO";
+
     private EmergencyGesture() {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGestureModule.kt b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGestureModule.kt
new file mode 100644
index 0000000..858c2ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGestureModule.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.systemui.emergency
+
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.res.Resources
+import android.text.TextUtils
+import android.util.Log
+import dagger.Module
+import dagger.Provides
+
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+
+/** Module for providing emergency gesture objects. */
+@Module
+object EmergencyGestureModule {
+
+    val TAG: String = "EmergencyGestureModule"
+
+    @Provides
+    fun emergencyGestureIntentFactory(
+        packageManager: PackageManager,
+        @Main resources: Resources,
+    ): EmergencyGestureIntentFactory {
+        return object : EmergencyGestureIntentFactory {
+            override fun invoke(action: String): Intent? {
+                return getEmergencyActionIntent(packageManager, resources, action)
+            }
+        }
+    }
+
+    /**
+     * Return the "best" Emergency action intent for a given action
+     */
+    private fun getEmergencyActionIntent(
+        packageManager: PackageManager,
+        @Main resources: Resources,
+        action: String,
+    ): Intent? {
+        val emergencyIntent = Intent(action)
+        val emergencyActivities = packageManager.queryIntentActivities(emergencyIntent,
+                PackageManager.MATCH_SYSTEM_ONLY)
+        val resolveInfo: ResolveInfo? = getTopEmergencySosInfo(emergencyActivities, resources)
+        if (resolveInfo == null) {
+            Log.wtf(TAG, "Couldn't find an app to process the emergency intent.")
+            return null
+        }
+        emergencyIntent.setComponent(ComponentName(resolveInfo.activityInfo.packageName,
+                resolveInfo.activityInfo.name))
+        emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+        return emergencyIntent
+    }
+
+    /**
+     * Select and return the "best" ResolveInfo for Emergency SOS Activity.
+     */
+    private fun getTopEmergencySosInfo(
+        emergencyActivities: List<ResolveInfo>,
+        @Main resources: Resources,
+    ): ResolveInfo? {
+        // No matched activity.
+        if (emergencyActivities.isEmpty()) {
+            return null
+        }
+
+        // Of multiple matched Activities, give preference to the pre-set package name.
+        val preferredAppPackageName =
+                resources.getString(R.string.config_preferredEmergencySosPackage)
+
+        // If there is no preferred app, then return first match.
+        if (TextUtils.isEmpty(preferredAppPackageName)) {
+            return emergencyActivities.get(0)
+        }
+
+        for (emergencyInfo: ResolveInfo in emergencyActivities) {
+            // If activity is from the preferred app, use it.
+            if (TextUtils.equals(emergencyInfo.activityInfo.packageName, preferredAppPackageName)) {
+                return emergencyInfo
+            }
+        }
+        // No matching activity: return first match
+        return emergencyActivities.get(0)
+    }
+
+    /**
+     * Creates an intent to launch the Emergency action. If no handler is present, returns `null`.
+     */
+    public interface EmergencyGestureIntentFactory {
+        operator fun invoke(action: String): Intent?
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index ea8d7d7..7b5139a 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -17,21 +17,19 @@
 package com.android.systemui.haptics.qs
 
 import android.os.VibrationEffect
-import android.view.View
 import androidx.annotation.VisibleForTesting
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.animation.Expandable
+import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.policy.KeyguardStateController
 import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
 
 /**
  * A class that handles the long press visuo-haptic effect for a QS tile.
  *
- * The class is also a [View.OnTouchListener] to handle the touch events, clicks and long-press
- * gestures of the tile. The class also provides a [State] tha can be used to determine the current
- * state of the long press effect.
+ * The class can contain references to a [QSTile] and an [Expandable] to perform clicks and
+ * long-clicks on the tile. The class also provides a [State] tha can be used to determine the
+ * current state of the long press effect.
  *
  * @property[vibratorHelper] The [VibratorHelper] to deliver haptic effects.
  * @property[effectDuration] The duration of the effect in ms.
@@ -41,7 +39,7 @@
 @Inject
 constructor(
     private val vibratorHelper: VibratorHelper?,
-    keyguardInteractor: KeyguardInteractor,
+    private val keyguardStateController: KeyguardStateController,
 ) {
 
     var effectDuration = 0
@@ -51,19 +49,12 @@
     var state = State.IDLE
         private set
 
-    /** Flow for view control and action */
-    private val _postedActionType = MutableStateFlow<ActionType?>(null)
-    val actionType: Flow<ActionType?> =
-        combine(
-            _postedActionType,
-            keyguardInteractor.isKeyguardDismissible,
-        ) { action, isDismissible ->
-            if (!isDismissible && action == ActionType.LONG_PRESS) {
-                ActionType.RESET_AND_LONG_PRESS
-            } else {
-                action
-            }
-        }
+    /** Callback object for effect actions */
+    var callback: Callback? = null
+
+    /** The [QSTile] and [Expandable] used to perform a long-click and click actions */
+    var qsTile: QSTile? = null
+    var expandable: Expandable? = null
 
     /** Haptic effects */
     private val durations =
@@ -106,33 +97,25 @@
             State.IDLE -> {
                 setState(State.TIMEOUT_WAIT)
             }
-            State.RUNNING_BACKWARDS -> _postedActionType.value = ActionType.CANCEL_ANIMATOR
+            State.RUNNING_BACKWARDS_FROM_UP,
+            State.RUNNING_BACKWARDS_FROM_CANCEL -> callback?.onCancelAnimator()
             else -> {}
         }
     }
 
     fun handleActionUp() {
-        when (state) {
-            State.TIMEOUT_WAIT -> {
-                _postedActionType.value = ActionType.CLICK
-                setState(State.IDLE)
-            }
-            State.RUNNING_FORWARD -> {
-                _postedActionType.value = ActionType.REVERSE_ANIMATOR
-                setState(State.RUNNING_BACKWARDS)
-            }
-            else -> {}
+        if (state == State.RUNNING_FORWARD) {
+            setState(State.RUNNING_BACKWARDS_FROM_UP)
+            callback?.onReverseAnimator()
         }
     }
 
     fun handleActionCancel() {
         when (state) {
-            State.TIMEOUT_WAIT -> {
-                setState(State.IDLE)
-            }
+            State.TIMEOUT_WAIT -> setState(State.IDLE)
             State.RUNNING_FORWARD -> {
-                _postedActionType.value = ActionType.REVERSE_ANIMATOR
-                setState(State.RUNNING_BACKWARDS)
+                setState(State.RUNNING_BACKWARDS_FROM_CANCEL)
+                callback?.onReverseAnimator()
             }
             else -> {}
         }
@@ -145,13 +128,24 @@
 
     /** This function is called both when an animator completes or gets cancelled */
     fun handleAnimationComplete() {
-        if (state == State.RUNNING_FORWARD) {
-            vibrate(snapEffect)
-            _postedActionType.value = ActionType.LONG_PRESS
-        }
-        if (state != State.TIMEOUT_WAIT) {
-            // This will happen if the animator did not finish by being cancelled
-            setState(State.IDLE)
+        when (state) {
+            State.RUNNING_FORWARD -> {
+                setState(State.IDLE)
+                vibrate(snapEffect)
+                if (keyguardStateController.isUnlocked) {
+                    qsTile?.longClick(expandable)
+                } else {
+                    callback?.onResetProperties()
+                    qsTile?.longClick(expandable)
+                }
+            }
+            State.RUNNING_BACKWARDS_FROM_UP -> {
+                setState(State.IDLE)
+                callback?.onEffectFinishedReversing()
+                qsTile?.click(expandable)
+            }
+            State.RUNNING_BACKWARDS_FROM_CANCEL -> setState(State.IDLE)
+            else -> {}
         }
     }
 
@@ -161,12 +155,19 @@
 
     fun handleTimeoutComplete() {
         if (state == State.TIMEOUT_WAIT) {
-            _postedActionType.value = ActionType.START_ANIMATOR
+            callback?.onStartAnimator()
         }
     }
 
-    fun clearActionType() {
-        _postedActionType.value = null
+    fun onTileClick(): Boolean {
+        if (state == State.TIMEOUT_WAIT) {
+            setState(State.IDLE)
+            qsTile?.let {
+                it.click(expandable)
+                return true
+            }
+        }
+        return false
     }
 
     /**
@@ -195,18 +196,30 @@
 
     enum class State {
         IDLE, /* The effect is idle waiting for touch input */
-        TIMEOUT_WAIT, /* The effect is waiting for a [PRESSED_TIMEOUT] period */
+        TIMEOUT_WAIT, /* The effect is waiting for a tap timeout period */
         RUNNING_FORWARD, /* The effect is running normally */
-        RUNNING_BACKWARDS, /* The effect was interrupted and is now running backwards */
+        /* The effect was interrupted by an ACTION_UP and is now running backwards */
+        RUNNING_BACKWARDS_FROM_UP,
+        /* The effect was interrupted by an ACTION_CANCEL and is now running backwards */
+        RUNNING_BACKWARDS_FROM_CANCEL,
     }
 
-    /* A type of action to perform on the view depending on the effect's state and logic */
-    enum class ActionType {
-        CLICK,
-        LONG_PRESS,
-        RESET_AND_LONG_PRESS,
-        START_ANIMATOR,
-        REVERSE_ANIMATOR,
-        CANCEL_ANIMATOR,
+    /** Callbacks to notify view and animator actions */
+    interface Callback {
+
+        /** Reset the tile visual properties */
+        fun onResetProperties()
+
+        /** Event where the effect completed by being reversed */
+        fun onEffectFinishedReversing()
+
+        /** Start the effect animator */
+        fun onStartAnimator()
+
+        /** Reverse the effect animator */
+        fun onReverseAnimator()
+
+        /** Cancel the effect animator */
+        fun onCancelAnimator()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
deleted file mode 100644
index 4875f48..0000000
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
+++ /dev/null
@@ -1,127 +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.haptics.qs
-
-import android.animation.ValueAnimator
-import android.annotation.SuppressLint
-import android.view.MotionEvent
-import android.view.ViewConfiguration
-import android.view.animation.AccelerateDecelerateInterpolator
-import androidx.core.animation.doOnCancel
-import androidx.core.animation.doOnEnd
-import androidx.core.animation.doOnStart
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.qs.tileimpl.QSTileViewImpl
-import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.flow.filterNotNull
-
-object QSLongPressEffectViewBinder {
-
-    fun bind(
-        tile: QSTileViewImpl,
-        qsLongPressEffect: QSLongPressEffect?,
-        tileSpec: String?,
-    ): DisposableHandle? {
-        if (qsLongPressEffect == null) return null
-
-        // Set the touch listener as the long-press effect
-        setTouchListener(tile, qsLongPressEffect)
-
-        return tile.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                // Action to perform
-                launch({ "${tileSpec ?: "unknownTileSpec"}#LongPressEffect#action" }) {
-                    var effectAnimator: ValueAnimator? = null
-
-                    qsLongPressEffect.actionType.filterNotNull().collect { action ->
-                        when (action) {
-                            QSLongPressEffect.ActionType.CLICK -> {
-                                tile.performClick()
-                                qsLongPressEffect.clearActionType()
-                            }
-                            QSLongPressEffect.ActionType.LONG_PRESS -> {
-                                tile.prepareForLaunch()
-                                tile.performLongClick()
-                                qsLongPressEffect.clearActionType()
-                            }
-                            QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS -> {
-                                tile.resetLongPressEffectProperties()
-                                tile.performLongClick()
-                                qsLongPressEffect.clearActionType()
-                            }
-                            QSLongPressEffect.ActionType.START_ANIMATOR -> {
-                                if (effectAnimator?.isRunning != true) {
-                                    effectAnimator =
-                                        ValueAnimator.ofFloat(0f, 1f).apply {
-                                            this.duration =
-                                                qsLongPressEffect.effectDuration.toLong()
-                                            interpolator = AccelerateDecelerateInterpolator()
-
-                                            doOnStart { qsLongPressEffect.handleAnimationStart() }
-                                            addUpdateListener {
-                                                val value = animatedValue as Float
-                                                if (value == 0f) {
-                                                    tile.bringToFront()
-                                                } else {
-                                                    tile.updateLongPressEffectProperties(value)
-                                                }
-                                            }
-                                            doOnEnd { qsLongPressEffect.handleAnimationComplete() }
-                                            doOnCancel { qsLongPressEffect.handleAnimationCancel() }
-                                            start()
-                                        }
-                                }
-                            }
-                            QSLongPressEffect.ActionType.REVERSE_ANIMATOR -> {
-                                effectAnimator?.let {
-                                    val pausedProgress = it.animatedFraction
-                                    qsLongPressEffect.playReverseHaptics(pausedProgress)
-                                    it.reverse()
-                                }
-                            }
-                            QSLongPressEffect.ActionType.CANCEL_ANIMATOR -> {
-                                tile.resetLongPressEffectProperties()
-                                effectAnimator?.cancel()
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @SuppressLint("ClickableViewAccessibility")
-    private fun setTouchListener(tile: QSTileViewImpl, longPressEffect: QSLongPressEffect?) {
-        tile.setOnTouchListener { _, event ->
-            when (event.actionMasked) {
-                MotionEvent.ACTION_DOWN -> {
-                    tile.postDelayed(
-                        { longPressEffect?.handleTimeoutComplete() },
-                        ViewConfiguration.getTapTimeout().toLong(),
-                    )
-                    longPressEffect?.handleActionDown()
-                }
-                MotionEvent.ACTION_UP -> longPressEffect?.handleActionUp()
-                MotionEvent.ACTION_CANCEL -> longPressEffect?.handleActionCancel()
-            }
-            true
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
index fc9406b..c6fb4f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardModule.kt
@@ -19,13 +19,12 @@
 
 import com.android.systemui.keyboard.data.repository.KeyboardRepository
 import com.android.systemui.keyboard.data.repository.KeyboardRepositoryImpl
-import com.android.systemui.keyboard.shortcut.ShortcutHelperModule
 import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepository
 import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepositoryImpl
 import dagger.Binds
 import dagger.Module
 
-@Module(includes = [ShortcutHelperModule::class])
+@Module
 abstract class KeyboardModule {
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt
index 9450af4..ec92ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperRepository.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
+import android.os.UserHandle
 import com.android.systemui.CoreStartable
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
@@ -49,6 +50,10 @@
             action = Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS,
             onReceive = { state.value = Inactive }
         )
+        registerBroadcastReceiver(
+            action = Intent.ACTION_CLOSE_SYSTEM_DIALOGS,
+            onReceive = { state.value = Inactive }
+        )
         commandQueue.addCallback(
             object : CommandQueue.Callbacks {
                 override fun dismissKeyboardShortcutsMenu() {
@@ -80,7 +85,8 @@
                     }
                 },
             filter = IntentFilter(action),
-            flags = Context.RECEIVER_EXPORTED or Context.RECEIVER_VISIBLE_TO_INSTANT_APPS
+            flags = Context.RECEIVER_EXPORTED or Context.RECEIVER_VISIBLE_TO_INSTANT_APPS,
+            user = UserHandle.ALL,
         )
     }
 }
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 52ccc21..271e79b 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
@@ -16,13 +16,16 @@
 
 package com.android.systemui.keyboard.shortcut.ui.composable
 
-import androidx.annotation.StringRes
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.FlowRow
+import androidx.compose.foundation.layout.FlowRowScope
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
@@ -32,21 +35,19 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.OpenInNew
-import androidx.compose.material.icons.filled.Accessibility
-import androidx.compose.material.icons.filled.Apps
 import androidx.compose.material.icons.filled.ExpandMore
-import androidx.compose.material.icons.filled.Keyboard
 import androidx.compose.material.icons.filled.Search
-import androidx.compose.material.icons.filled.Tv
-import androidx.compose.material.icons.filled.VerticalSplit
 import androidx.compose.material3.CenterAlignedTopAppBar
 import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.NavigationDrawerItemColors
@@ -56,6 +57,7 @@
 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.getValue
@@ -69,10 +71,13 @@
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.graphicsLayer
 import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.role
 import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.util.fastForEach
@@ -81,8 +86,13 @@
 import com.android.systemui.res.R
 
 @Composable
-fun ShortcutHelper(modifier: Modifier = Modifier, onKeyboardSettingsClicked: () -> Unit) {
-    if (shouldUseSinglePane()) {
+fun ShortcutHelper(
+    onKeyboardSettingsClicked: () -> Unit,
+    modifier: Modifier = Modifier,
+    categories: List<ShortcutHelperCategory> = ShortcutHelperTemporaryData.categories,
+    useSinglePane: @Composable () -> Boolean = { shouldUseSinglePane() },
+) {
+    if (useSinglePane()) {
         ShortcutHelperSinglePane(modifier, categories, onKeyboardSettingsClicked)
     } else {
         ShortcutHelperTwoPane(modifier, categories, onKeyboardSettingsClicked)
@@ -91,7 +101,8 @@
 
 @Composable
 private fun shouldUseSinglePane() =
-    LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact
+    LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact ||
+        LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
 
 @Composable
 private fun ShortcutHelperSinglePane(
@@ -209,11 +220,31 @@
 
 @Composable
 private fun ShortcutCategoryDetailsSinglePane(category: ShortcutHelperCategory) {
-    Box(modifier = Modifier.fillMaxWidth().heightIn(min = 300.dp)) {
-        Text(
-            modifier = Modifier.align(Alignment.Center),
-            text = stringResource(category.labelResId),
-        )
+    Column(Modifier.padding(horizontal = 16.dp)) {
+        category.subCategories.fastForEach { subCategory ->
+            ShortcutSubCategorySinglePane(subCategory)
+        }
+    }
+}
+
+@Composable
+private fun ShortcutSubCategorySinglePane(subCategory: SubCategory) {
+    // This @Composable is expected to be in a Column.
+    SubCategoryTitle(subCategory.label)
+    subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
+        if (index > 0) {
+            HorizontalDivider()
+        }
+        ShortcutSinglePane(shortcut)
+    }
+}
+
+@Composable
+private fun ShortcutSinglePane(shortcut: Shortcut) {
+    Column(Modifier.padding(vertical = 24.dp)) {
+        ShortcutDescriptionText(shortcut = shortcut)
+        Spacer(modifier = Modifier.height(12.dp))
+        ShortcutKeyCombinations(shortcut = shortcut)
     }
 }
 
@@ -223,6 +254,7 @@
     categories: List<ShortcutHelperCategory>,
     onKeyboardSettingsClicked: () -> Unit,
 ) {
+    var selectedCategory by remember { mutableStateOf(categories.first()) }
     Column(modifier = modifier.fillMaxSize().padding(start = 24.dp, end = 24.dp, top = 26.dp)) {
         TitleBar()
         Spacer(modifier = Modifier.height(12.dp))
@@ -230,39 +262,189 @@
             StartSidePanel(
                 modifier = Modifier.fillMaxWidth(fraction = 0.32f),
                 categories = categories,
+                selectedCategory = selectedCategory,
+                onCategoryClicked = { selectedCategory = it },
                 onKeyboardSettingsClicked = onKeyboardSettingsClicked,
             )
             Spacer(modifier = Modifier.width(24.dp))
-            EndSidePanel(Modifier.fillMaxSize())
+            EndSidePanel(Modifier.fillMaxSize(), selectedCategory)
         }
     }
 }
 
 @Composable
+private fun EndSidePanel(modifier: Modifier, category: ShortcutHelperCategory) {
+    LazyColumn(modifier.nestedScroll(rememberNestedScrollInteropConnection())) {
+        items(items = category.subCategories, key = { item -> item.label }) {
+            SubCategoryContainerDualPane(it)
+            Spacer(modifier = Modifier.height(8.dp))
+        }
+    }
+}
+
+@Composable
+private fun SubCategoryContainerDualPane(subCategory: SubCategory) {
+    Surface(
+        modifier = Modifier.fillMaxWidth(),
+        shape = RoundedCornerShape(28.dp),
+        color = MaterialTheme.colorScheme.surfaceBright
+    ) {
+        Column(Modifier.padding(horizontal = 32.dp, vertical = 24.dp)) {
+            SubCategoryTitle(subCategory.label)
+            Spacer(Modifier.height(24.dp))
+            subCategory.shortcuts.fastForEachIndexed { index, shortcut ->
+                if (index > 0) {
+                    HorizontalDivider()
+                }
+                ShortcutViewDualPane(shortcut)
+            }
+        }
+    }
+}
+
+@Composable
+private fun SubCategoryTitle(title: String) {
+    Text(
+        title,
+        style = MaterialTheme.typography.titleSmall,
+        color = MaterialTheme.colorScheme.primary,
+    )
+}
+
+@Composable
+private fun ShortcutViewDualPane(shortcut: Shortcut) {
+    Row(Modifier.padding(vertical = 16.dp)) {
+        ShortcutDescriptionText(
+            modifier = Modifier.weight(0.25f).align(Alignment.CenterVertically),
+            shortcut = shortcut,
+        )
+        ShortcutKeyCombinations(
+            modifier = Modifier.weight(0.75f),
+            shortcut = shortcut,
+        )
+    }
+}
+
+@OptIn(ExperimentalLayoutApi::class)
+@Composable
+private fun ShortcutKeyCombinations(
+    modifier: Modifier = Modifier,
+    shortcut: Shortcut,
+) {
+    FlowRow(modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp)) {
+        shortcut.commands.forEachIndexed { index, command ->
+            if (index > 0) {
+                ShortcutOrSeparator(spacing = 16.dp)
+            }
+            ShortcutCommand(command)
+        }
+    }
+}
+
+@Composable
+private fun ShortcutCommand(command: ShortcutCommand) {
+    // This @Composable is expected to be in a Row or FlowRow.
+    command.keys.forEachIndexed { keyIndex, key ->
+        if (keyIndex > 0) {
+            Spacer(Modifier.width(4.dp))
+        }
+        ShortcutKeyContainer {
+            if (key is ShortcutKey.Text) {
+                ShortcutTextKey(key)
+            } else if (key is ShortcutKey.Icon) {
+                ShortcutIconKey(key)
+            }
+        }
+    }
+}
+
+@Composable
+private fun ShortcutKeyContainer(shortcutKeyContent: @Composable BoxScope.() -> Unit) {
+    Box(
+        modifier =
+            Modifier.height(36.dp)
+                .background(
+                    color = MaterialTheme.colorScheme.surfaceContainer,
+                    shape = RoundedCornerShape(12.dp)
+                ),
+    ) {
+        shortcutKeyContent()
+    }
+}
+
+@Composable
+private fun BoxScope.ShortcutTextKey(key: ShortcutKey.Text) {
+    Text(
+        text = key.value,
+        modifier = Modifier.align(Alignment.Center).padding(horizontal = 12.dp),
+        style = MaterialTheme.typography.titleSmall,
+    )
+}
+
+@Composable
+private fun BoxScope.ShortcutIconKey(key: ShortcutKey.Icon) {
+    Icon(
+        imageVector = key.value,
+        contentDescription = null,
+        modifier = Modifier.align(Alignment.Center).padding(6.dp)
+    )
+}
+
+@OptIn(ExperimentalLayoutApi::class)
+@Composable
+private fun FlowRowScope.ShortcutOrSeparator(spacing: Dp) {
+    Spacer(Modifier.width(spacing))
+    Text(
+        text = stringResource(R.string.shortcut_helper_key_combinations_or_separator),
+        modifier = Modifier.align(Alignment.CenterVertically),
+        style = MaterialTheme.typography.titleSmall,
+    )
+    Spacer(Modifier.width(spacing))
+}
+
+@Composable
+private fun ShortcutDescriptionText(
+    shortcut: Shortcut,
+    modifier: Modifier = Modifier,
+) {
+    Text(
+        modifier = modifier,
+        text = shortcut.label,
+        style = MaterialTheme.typography.bodyMedium,
+        color = MaterialTheme.colorScheme.onSurface,
+    )
+}
+
+@Composable
 private fun StartSidePanel(
     modifier: Modifier,
     categories: List<ShortcutHelperCategory>,
     onKeyboardSettingsClicked: () -> Unit,
+    selectedCategory: ShortcutHelperCategory,
+    onCategoryClicked: (ShortcutHelperCategory) -> Unit,
 ) {
     Column(modifier) {
         ShortcutsSearchBar()
         Spacer(modifier = Modifier.heightIn(16.dp))
-        CategoriesPanelTwoPane(categories)
+        CategoriesPanelTwoPane(categories, selectedCategory, onCategoryClicked)
         Spacer(modifier = Modifier.weight(1f))
         KeyboardSettings(onKeyboardSettingsClicked)
     }
 }
 
 @Composable
-private fun CategoriesPanelTwoPane(categories: List<ShortcutHelperCategory>) {
-    var selected by remember { mutableStateOf(categories.first()) }
+private fun CategoriesPanelTwoPane(
+    categories: List<ShortcutHelperCategory>,
+    selectedCategory: ShortcutHelperCategory,
+    onCategoryClicked: (ShortcutHelperCategory) -> Unit
+) {
     Column {
         categories.fastForEach {
             CategoryItemTwoPane(
                 label = stringResource(it.labelResId),
                 icon = it.icon,
-                selected = selected == it,
-                onClick = { selected = it }
+                selected = selectedCategory == it,
+                onClick = { onCategoryClicked(it) }
             )
         }
     }
@@ -305,15 +487,6 @@
 }
 
 @Composable
-fun EndSidePanel(modifier: Modifier) {
-    Surface(
-        modifier = modifier,
-        shape = RoundedCornerShape(28.dp),
-        color = MaterialTheme.colorScheme.surfaceBright
-    ) {}
-}
-
-@Composable
 @OptIn(ExperimentalMaterial3Api::class)
 private fun TitleBar() {
     CenterAlignedTopAppBar(
@@ -333,6 +506,7 @@
 private fun ShortcutsSearchBar() {
     var query by remember { mutableStateOf("") }
     SearchBar(
+        modifier = Modifier.fillMaxWidth(),
         colors = SearchBarDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceBright),
         query = query,
         active = false,
@@ -372,25 +546,6 @@
     }
 }
 
-/** Temporary data class just to populate the UI. */
-private data class ShortcutHelperCategory(
-    @StringRes val labelResId: Int,
-    val icon: ImageVector,
-)
-
-// Temporarily populating the categories directly in the UI.
-private val categories =
-    listOf(
-        ShortcutHelperCategory(R.string.shortcut_helper_category_system, Icons.Default.Tv),
-        ShortcutHelperCategory(
-            R.string.shortcut_helper_category_multitasking,
-            Icons.Default.VerticalSplit
-        ),
-        ShortcutHelperCategory(R.string.shortcut_helper_category_input, Icons.Default.Keyboard),
-        ShortcutHelperCategory(R.string.shortcut_helper_category_app_shortcuts, Icons.Default.Apps),
-        ShortcutHelperCategory(R.string.shortcut_helper_category_a11y, Icons.Default.Accessibility),
-    )
-
 object ShortcutHelper {
 
     object Shapes {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperTemporaryData.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperTemporaryData.kt
new file mode 100644
index 0000000..fa2388f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelperTemporaryData.kt
@@ -0,0 +1,251 @@
+/*
+ * 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.annotation.StringRes
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Accessibility
+import androidx.compose.material.icons.filled.Apps
+import androidx.compose.material.icons.filled.ArrowBackIosNew
+import androidx.compose.material.icons.filled.Keyboard
+import androidx.compose.material.icons.filled.KeyboardCommandKey
+import androidx.compose.material.icons.filled.RadioButtonUnchecked
+import androidx.compose.material.icons.filled.Tv
+import androidx.compose.material.icons.filled.VerticalSplit
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.systemui.res.R
+
+/** Temporary data classes and data below just to populate the UI. */
+data class ShortcutHelperCategory(
+    @StringRes val labelResId: Int,
+    val icon: ImageVector,
+    val subCategories: List<SubCategory>,
+)
+
+data class SubCategory(
+    val label: String,
+    val shortcuts: List<Shortcut>,
+)
+
+data class Shortcut(val label: String, val commands: List<ShortcutCommand>)
+
+data class ShortcutCommand(val keys: List<ShortcutKey>)
+
+sealed interface ShortcutKey {
+    data class Text(val value: String) : ShortcutKey
+
+    data class Icon(val value: ImageVector) : ShortcutKey
+}
+
+// DSL Builder Functions
+private fun shortcutHelperCategory(
+    labelResId: Int,
+    icon: ImageVector,
+    block: ShortcutHelperCategoryBuilder.() -> Unit
+): ShortcutHelperCategory = ShortcutHelperCategoryBuilder(labelResId, icon).apply(block).build()
+
+private fun ShortcutHelperCategoryBuilder.subCategory(
+    label: String,
+    block: SubCategoryBuilder.() -> Unit
+) {
+    subCategories.add(SubCategoryBuilder(label).apply(block).build())
+}
+
+private fun SubCategoryBuilder.shortcut(label: String, block: ShortcutBuilder.() -> Unit) {
+    shortcuts.add(ShortcutBuilder(label).apply(block).build())
+}
+
+private fun ShortcutBuilder.command(block: ShortcutCommandBuilder.() -> Unit) {
+    commands.add(ShortcutCommandBuilder().apply(block).build())
+}
+
+private fun ShortcutCommandBuilder.key(value: String) {
+    keys.add(ShortcutKey.Text(value))
+}
+
+private fun ShortcutCommandBuilder.key(value: ImageVector) {
+    keys.add(ShortcutKey.Icon(value))
+}
+
+private class ShortcutHelperCategoryBuilder(
+    private val labelResId: Int,
+    private val icon: ImageVector
+) {
+    val subCategories = mutableListOf<SubCategory>()
+
+    fun build() = ShortcutHelperCategory(labelResId, icon, subCategories)
+}
+
+private class SubCategoryBuilder(private val label: String) {
+    val shortcuts = mutableListOf<Shortcut>()
+
+    fun build() = SubCategory(label, shortcuts)
+}
+
+private class ShortcutBuilder(private val label: String) {
+    val commands = mutableListOf<ShortcutCommand>()
+
+    fun build() = Shortcut(label, commands)
+}
+
+private class ShortcutCommandBuilder {
+    val keys = mutableListOf<ShortcutKey>()
+
+    fun build() = ShortcutCommand(keys)
+}
+
+object ShortcutHelperTemporaryData {
+
+    // Some shortcuts and their strings below are made up just to populate the UI for now.
+    // For this reason they are not in translatable resources yet.
+    val categories =
+        listOf(
+            shortcutHelperCategory(R.string.shortcut_helper_category_system, Icons.Default.Tv) {
+                subCategory("System controls") {
+                    shortcut("Go to home screen") {
+                        command { key(Icons.Default.RadioButtonUnchecked) }
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("H")
+                        }
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("Return")
+                        }
+                    }
+                    shortcut("View recent apps") {
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("Tab")
+                        }
+                    }
+                    shortcut("All apps search") {
+                        command { key(Icons.Default.KeyboardCommandKey) }
+                    }
+                }
+                subCategory("System apps") {
+                    shortcut("Go back") {
+                        command { key(Icons.Default.ArrowBackIosNew) }
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("Left arrow")
+                        }
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("ESC")
+                        }
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("Backspace")
+                        }
+                    }
+                    shortcut("View notifications") {
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("N")
+                        }
+                    }
+                    shortcut("Take a screenshot") {
+                        command { key(Icons.Default.KeyboardCommandKey) }
+                        command { key("CTRL") }
+                        command { key("S") }
+                    }
+                    shortcut("Open Settings") {
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("I")
+                        }
+                    }
+                }
+            },
+            shortcutHelperCategory(
+                R.string.shortcut_helper_category_multitasking,
+                Icons.Default.VerticalSplit
+            ) {
+                subCategory("Multitasking & windows") {
+                    shortcut("Take a screenshot") {
+                        command { key(Icons.Default.KeyboardCommandKey) }
+                        command { key("CTRL") }
+                        command { key("S") }
+                    }
+                }
+            },
+            shortcutHelperCategory(
+                R.string.shortcut_helper_category_input,
+                Icons.Default.Keyboard
+            ) {
+                subCategory("Input") {
+                    shortcut("Open Settings") {
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("I")
+                        }
+                    }
+                    shortcut("View notifications") {
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("N")
+                        }
+                    }
+                }
+            },
+            shortcutHelperCategory(
+                R.string.shortcut_helper_category_app_shortcuts,
+                Icons.Default.Apps
+            ) {
+                subCategory("App shortcuts") {
+                    shortcut("Open Settings") {
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("I")
+                        }
+                    }
+                    shortcut("Go back") {
+                        command { key(Icons.Default.ArrowBackIosNew) }
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("Left arrow")
+                        }
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("ESC")
+                        }
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("Backspace")
+                        }
+                    }
+                }
+            },
+            shortcutHelperCategory(
+                R.string.shortcut_helper_category_a11y,
+                Icons.Default.Accessibility
+            ) {
+                subCategory("Accessibility shortcuts") {
+                    shortcut("View recent apps") {
+                        command {
+                            key(Icons.Default.KeyboardCommandKey)
+                            key("Tab")
+                        }
+                    }
+                    shortcut("All apps search") {
+                        command { key(Icons.Default.KeyboardCommandKey) }
+                    }
+                }
+            }
+        )
+}
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 1e8d239..8706280 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
@@ -16,8 +16,11 @@
 
 package com.android.systemui.keyboard.shortcut.ui.view
 
+import android.content.ActivityNotFoundException
+import android.content.Intent
 import android.graphics.Insets
 import android.os.Bundle
+import android.provider.Settings
 import android.view.View
 import android.view.WindowInsets
 import androidx.activity.BackEventCompat
@@ -83,7 +86,15 @@
         }
     }
 
-    private fun onKeyboardSettingsClicked() {}
+    private fun onKeyboardSettingsClicked() {
+        try {
+            startActivity(Intent(Settings.ACTION_HARD_KEYBOARD_SETTINGS))
+        } catch (e: ActivityNotFoundException) {
+            // From the Settings docs: In some cases, a matching Activity may not exist, so ensure
+            // you safeguard against this.
+            e.printStackTrace()
+        }
+    }
 
     override fun onDestroy() {
         super.onDestroy()
@@ -112,7 +123,7 @@
             resources.getFloat(R.dimen.shortcut_helper_screen_width_fraction)
         // maxWidth needs to be set before the sheet is drawn, otherwise the call will have no
         // effect.
-        val screenWidth = resources.displayMetrics.widthPixels
+        val screenWidth = windowManager.maximumWindowMetrics.bounds.width()
         bottomSheetBehavior.maxWidth = (sheetScreenWidthFraction * screenWidth).toInt()
     }
 
@@ -121,7 +132,7 @@
             val safeDrawingInsets = insets.safeDrawing
             // Make sure the bottom sheet is not covered by the status bar.
             bottomSheetBehavior.maxHeight =
-                resources.displayMetrics.heightPixels - safeDrawingInsets.top
+                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(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index f2a544e..209bc7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -80,6 +80,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
 import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder;
@@ -118,6 +119,7 @@
     private final ShellTransitions mShellTransitions;
     private final DisplayTracker mDisplayTracker;
     private final PowerInteractor mPowerInteractor;
+    private final KeyguardInteractor mKeyguardInteractor;
     private final Lazy<SceneInteractor> mSceneInteractorLazy;
     private final Executor mMainExecutor;
 
@@ -338,6 +340,7 @@
             WindowManagerOcclusionManager windowManagerOcclusionManager,
             Lazy<SceneInteractor> sceneInteractorLazy,
             @Main Executor mainExecutor,
+            KeyguardInteractor keyguardInteractor,
             KeyguardEnabledInteractor keyguardEnabledInteractor) {
         super();
         mKeyguardViewMediator = keyguardViewMediator;
@@ -347,6 +350,7 @@
         mDisplayTracker = displayTracker;
         mFlags = featureFlags;
         mPowerInteractor = powerInteractor;
+        mKeyguardInteractor = keyguardInteractor;
         mSceneInteractorLazy = sceneInteractorLazy;
         mMainExecutor = mainExecutor;
 
@@ -474,6 +478,7 @@
         public void onDreamingStarted() {
             trace("onDreamingStarted");
             checkPermission();
+            mKeyguardInteractor.setDreaming(true);
             mKeyguardViewMediator.onDreamingStarted();
         }
 
@@ -481,6 +486,7 @@
         public void onDreamingStopped() {
             trace("onDreamingStopped");
             checkPermission();
+            mKeyguardInteractor.setDreaming(false);
             mKeyguardViewMediator.onDreamingStopped();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index cf83582..00566c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -712,7 +712,6 @@
         // As soon as the shade starts animating out of the way, start the canned unlock animation,
         // which will finish keyguard exit when it completes. The in-window animations in the
         // Launcher window will end on their own.
-        if (fastUnlockTransition()) hideKeyguardViewAfterRemoteAnimation()
         handler.postDelayed({
             if (keyguardViewMediator.get().isShowingAndNotOccluded &&
                 !keyguardStateController.isKeyguardGoingAway) {
@@ -723,7 +722,7 @@
 
             if ((wallpaperTargets?.isNotEmpty() == true)) {
                 fadeInWallpaper()
-                if (!fastUnlockTransition()) hideKeyguardViewAfterRemoteAnimation()
+                hideKeyguardViewAfterRemoteAnimation()
             } else {
                 keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation(
                     false /* cancelled */)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 306f4ff..608e25a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -39,6 +39,7 @@
 import com.android.keyguard.LockIconView
 import com.android.keyguard.dagger.KeyguardStatusViewComponent
 import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
@@ -70,6 +71,7 @@
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import dagger.Lazy
+import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -105,6 +107,7 @@
     private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>,
     private val clockInteractor: KeyguardClockInteractor,
     private val keyguardViewMediator: KeyguardViewMediator,
+    private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
 ) : CoreStartable {
 
     private var rootViewHandle: DisposableHandle? = null
@@ -157,6 +160,9 @@
                 )
             }
         }
+        if (deviceEntryUnlockTrackerViewBinder.isPresent) {
+            deviceEntryUnlockTrackerViewBinder.get().bind(keyguardRootView)
+        }
     }
 
     fun bindIndicationArea() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index f3a1843..2d60fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -176,6 +176,8 @@
 import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
 import com.android.wm.shell.keyguard.KeyguardTransitions;
 
+import dagger.Lazy;
+
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -185,7 +187,6 @@
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
-import dagger.Lazy;
 import kotlinx.coroutines.CoroutineDispatcher;
 
 /**
@@ -3014,7 +3015,7 @@
                         && mPM.isInteractive(), WakeAndUnlockUpdateReason.HIDE);
             }
 
-            if ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream) {
+            if (mBootCompleted && ((mShowing && !mOccluded) || mUnlockingAndWakingFromDream)) {
                 if (mUnlockingAndWakingFromDream) {
                     Log.d(TAG, "hiding keyguard before waking from dream");
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 1b342ed..180afb2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -142,15 +142,17 @@
         nonApps: Array<RemoteAnimationTarget>,
         finishedCallback: IRemoteAnimationFinishedCallback
     ) {
-        if (apps.isNotEmpty()) {
-            // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
-            // going away animation on its own, if an activity launches and then requests dismissing
-            // the keyguard. In this case, this is the first and only signal we'll receive to start
-            // a transition to GONE.
-            keyguardTransitionInteractor.startDismissKeyguardTransition(
-                reason = "Going away remote animation started"
-            )
+        // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
+        // going away animation on its own, if an activity launches and then requests dismissing the
+        // keyguard. In this case, this is the first and only signal we'll receive to start
+        // a transition to GONE. This transition needs to start even if we're not provided an app
+        // animation target - it's possible the app is destroyed on creation, etc. but we'll still
+        // be unlocking.
+        keyguardTransitionInteractor.startDismissKeyguardTransition(
+            reason = "Going away remote animation started"
+        )
 
+        if (apps.isNotEmpty()) {
             goingAwayRemoteAnimationFinishedCallback = finishedCallback
             keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
         } else {
@@ -183,25 +185,11 @@
     /**
      * Sets the lockscreen state WM-side by calling ATMS#setLockScreenShown.
      *
-     * [lockscreenShowing] defaults to true, since it's only ever null during the boot sequence,
-     * when we haven't yet called ATMS#setLockScreenShown. Typically,
-     * setWmLockscreenState(lockscreenShowing = true) is called early in the boot sequence, before
-     * setWmLockscreenState(aodVisible = true), so we don't expect to need to use this default, but
-     * if so, true should be the right choice.
+     * If [lockscreenShowing] is null, it means we don't know if the lockscreen is showing yet. This
+     * will be decided by the [KeyguardTransitionBootInteractor] shortly.
      */
     private fun setWmLockscreenState(
-        lockscreenShowing: Boolean =
-            this.isLockscreenShowing
-                ?: true.also {
-                    Log.d(
-                        TAG,
-                        "Using isLockscreenShowing=true default in setWmLockscreenState, " +
-                            "because setAodVisible was called before the first " +
-                            "setLockscreenShown call during boot. This is not typical, but is " +
-                            "theoretically possible. If you're investigating the lockscreen " +
-                            "showing unexpectedly, start here."
-                    )
-                },
+        lockscreenShowing: Boolean? = this.isLockscreenShowing,
         aodVisible: Boolean = this.isAodVisible
     ) {
         Log.d(
@@ -211,10 +199,27 @@
                 "aodVisible=$aodVisible)."
         )
 
+        if (lockscreenShowing == null) {
+            Log.d(
+                TAG,
+                "isAodVisible=$aodVisible, but lockscreenShowing=null. Waiting for" +
+                    "non-null lockscreenShowing before calling ATMS#setLockScreenShown, which" +
+                    "will happen once KeyguardTransitionBootInteractor starts the boot transition."
+            )
+            this.isAodVisible = aodVisible
+            return
+        }
+
         if (this.isLockscreenShowing == lockscreenShowing && this.isAodVisible == aodVisible) {
             return
         }
 
+        Log.d(
+            TAG,
+            "ATMS#setLockScreenShown(" +
+                "isLockscreenShowing=$lockscreenShowing, " +
+                "aodVisible=$aodVisible)."
+        )
         activityTaskManagerService.setLockScreenShown(lockscreenShowing, aodVisible)
         this.isLockscreenShowing = lockscreenShowing
         this.isAodVisible = aodVisible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
index d09b9f6..5d54126 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -38,6 +38,7 @@
 ) : KeyguardQuickAffordanceConfig {
 
     override val key: String = BuiltInKeyguardQuickAffordanceKeys.GLANCEABLE_HUB
+
     override fun pickerName(): String = "Glanceable hub"
 
     override val pickerIconResourceId = R.drawable.ic_widgets
@@ -52,6 +53,10 @@
         }
     }
 
+    override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
+        return KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
+    }
+
     override fun onTriggered(
         expandable: Expandable?
     ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
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 f15896b..cdf3b06 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
@@ -78,6 +78,8 @@
 
     val keyguardAlpha: StateFlow<Float>
 
+    val panelAlpha: MutableStateFlow<Float>
+
     /**
      * Observable for whether the keyguard is showing.
      *
@@ -148,7 +150,7 @@
      * Dozing/AOD is a specific type of dream, but it is also possible for other non-systemui dreams
      * to be active, such as screensavers.
      */
-    val isDreaming: Flow<Boolean>
+    val isDreaming: MutableStateFlow<Boolean>
 
     /** Observable for whether the device is dreaming with an overlay, see [DreamOverlayService] */
     val isDreamingWithOverlay: Flow<Boolean>
@@ -250,6 +252,12 @@
     /** Sets the current amount of alpha that should be used for rendering the keyguard. */
     fun setKeyguardAlpha(alpha: Float)
 
+    /** Temporary shim for fading out content when the brightness slider is used */
+    fun setPanelAlpha(alpha: Float)
+
+    /** Whether the device is actively dreaming */
+    fun setDreaming(isDreaming: Boolean)
+
     /**
      * Returns whether the keyguard bottom area should be constrained to the top of the lock icon
      */
@@ -335,6 +343,8 @@
     private val _keyguardAlpha = MutableStateFlow(1f)
     override val keyguardAlpha = _keyguardAlpha.asStateFlow()
 
+    override val panelAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
+
     private val _clockShouldBeCentered = MutableStateFlow(true)
     override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered.asStateFlow()
 
@@ -509,25 +519,7 @@
             }
             .distinctUntilChanged()
 
-    override val isDreaming: Flow<Boolean> =
-        conflatedCallbackFlow {
-                val callback =
-                    object : KeyguardUpdateMonitorCallback() {
-                        override fun onDreamingStateChanged(isDreaming: Boolean) {
-                            trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
-                        }
-                    }
-                keyguardUpdateMonitor.registerCallback(callback)
-                trySendWithFailureLogging(
-                    keyguardUpdateMonitor.isDreaming,
-                    TAG,
-                    "initial isDreaming",
-                )
-
-                awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
-            }
-            .flowOn(mainDispatcher)
-            .distinctUntilChanged()
+    override val isDreaming: MutableStateFlow<Boolean> = MutableStateFlow(false)
 
     override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
         val callback =
@@ -674,6 +666,14 @@
         _keyguardAlpha.value = alpha
     }
 
+    override fun setPanelAlpha(alpha: Float) {
+        panelAlpha.value = alpha
+    }
+
+    override fun setDreaming(isDreaming: Boolean) {
+        this.isDreaming.value = isDreaming
+    }
+
     override fun isUdfpsSupported(): Boolean = keyguardUpdateMonitor.isUdfpsSupported
 
     override fun setQuickSettingsVisible(isVisible: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 8ec460a..1b201ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -20,6 +20,7 @@
 import android.animation.ValueAnimator
 import android.animation.ValueAnimator.AnimatorUpdateListener
 import android.annotation.FloatRange
+import android.annotation.SuppressLint
 import android.os.Trace
 import android.util.Log
 import com.android.app.tracing.coroutines.withContext
@@ -117,10 +118,11 @@
 constructor(
     @Main val mainDispatcher: CoroutineDispatcher,
 ) : KeyguardTransitionRepository {
-    /*
-     * Each transition between [KeyguardState]s will have an associated Flow.
-     * In order to collect these events, clients should call [transition].
+    /**
+     * Each transition between [KeyguardState]s will have an associated Flow. In order to collect
+     * these events, clients should call [transition].
      */
+    @SuppressLint("SharedFlowCreation")
     private val _transitions =
         MutableSharedFlow<TransitionStep>(
             replay = 2,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 756c6c2..49d00af 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -24,9 +24,12 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import com.android.wm.shell.animation.Interpolators
 import javax.inject.Inject
@@ -36,7 +39,6 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filter
@@ -81,17 +83,16 @@
     }
 
     val surfaceBehindVisibility: Flow<Boolean?> =
-        combine(
-                transitionInteractor.startedKeyguardTransitionStep,
-                transitionInteractor.transitionStepsFromState(KeyguardState.ALTERNATE_BOUNCER)
-            ) { startedStep, fromBouncerStep ->
-                if (startedStep.to != KeyguardState.GONE) {
-                    return@combine null
-                }
-
+        transitionInteractor
+            .transition(
+                edge = Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = Scenes.Gone),
+                edgeWithoutSceneContainer =
+                    Edge.create(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
+            )
+            .map<TransitionStep, Boolean?> {
                 // The alt bouncer is pretty fast to hide, so start the surface behind animation
                 // around 30%.
-                fromBouncerStep.value > 0.3f
+                it.value > 0.3f
             }
             .onStart {
                 // Default to null ("don't care, use a reasonable default").
@@ -113,13 +114,26 @@
                     powerInteractor.isAwake,
                     keyguardInteractor.isAodAvailable,
                     communalInteractor.isIdleOnCommunal,
+                    communalInteractor.editModeOpen,
                     keyguardInteractor.isKeyguardOccluded,
                 )
                 .filterRelevantKeyguardStateAnd {
                     (isAlternateBouncerShowing, isPrimaryBouncerShowing, _, _, _) ->
                     !isAlternateBouncerShowing && !isPrimaryBouncerShowing
                 }
-                .collect { (_, _, isAwake, isAodAvailable, isIdleOnCommunal, isOccluded) ->
+                .collect {
+                    (
+                        _,
+                        _,
+                        isAwake,
+                        isAodAvailable,
+                        isIdleOnCommunal,
+                        isCommunalEditMode,
+                        isOccluded) ->
+                    // When unlocking over glanceable hub to enter edit mode, transitioning directly
+                    // to GONE prevents the lockscreen flash. Let listenForAlternateBouncerToGone
+                    // handle it.
+                    if (isCommunalEditMode) return@collect
                     val to =
                         if (!isAwake) {
                             if (isAodAvailable) {
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 f5e98f1..f3692bd 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
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineDispatcher
@@ -65,6 +66,7 @@
 
     override fun start() {
         listenForDozingToAny()
+        listenForDozingToGoneViaBiometrics()
         listenForWakeFromDozing()
         listenForTransitionToCamera(scope, keyguardInteractor)
     }
@@ -77,6 +79,35 @@
             isKeyguardDismissible && !isKeyguardShowing
         }
 
+    private fun listenForDozingToGoneViaBiometrics() {
+        if (KeyguardWmStateRefactor.isEnabled) {
+            return
+        }
+
+        // This is separate from `listenForDozingToAny` because any delay on wake and unlock will
+        // cause a noticeable issue with animations
+        scope.launch {
+            powerInteractor.isAwake
+                .filterRelevantKeyguardStateAnd { isAwake -> isAwake }
+                .sample(
+                    keyguardInteractor.biometricUnlockState,
+                    ::Pair,
+                )
+                .collect {
+                    (
+                        _,
+                        biometricUnlockState,
+                    ) ->
+                    if (isWakeAndUnlock(biometricUnlockState.mode)) {
+                        startTransitionTo(
+                            KeyguardState.GONE,
+                            ownerReason = "biometric wake and unlock",
+                        )
+                    }
+                }
+        }
+    }
+
     private fun listenForDozingToAny() {
         if (KeyguardWmStateRefactor.isEnabled) {
             return
@@ -87,7 +118,6 @@
                 .debounce(50L)
                 .filterRelevantKeyguardStateAnd { isAwake -> isAwake }
                 .sample(
-                    keyguardInteractor.biometricUnlockState,
                     keyguardInteractor.isKeyguardOccluded,
                     communalInteractor.isIdleOnCommunal,
                     canTransitionToGoneOnWake,
@@ -96,7 +126,6 @@
                 .collect {
                     (
                         _,
-                        biometricUnlockState,
                         occluded,
                         isIdleOnCommunal,
                         canTransitionToGoneOnWake,
@@ -104,8 +133,6 @@
                     startTransitionTo(
                         if (!deviceEntryRepository.isLockscreenEnabled()) {
                             KeyguardState.GONE
-                        } else if (isWakeAndUnlock(biometricUnlockState.mode)) {
-                            KeyguardState.GONE
                         } else if (canTransitionToGoneOnWake) {
                             KeyguardState.GONE
                         } else if (primaryBouncerShowing) {
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 25c3b0d..7fa197c 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
@@ -118,7 +118,6 @@
 
     fun startToLockscreenTransition() {
         scope.launch {
-            KeyguardWmStateRefactor.isUnexpectedlyInLegacyMode()
             if (
                 transitionInteractor.startedKeyguardState.replayCache.last() ==
                     KeyguardState.DREAMING
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index a540d76..f5b12a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -169,6 +169,7 @@
                     KeyguardState.AOD -> TO_AOD_DURATION
                     KeyguardState.DOZING -> TO_DOZING_DURATION
                     KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
+                    KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
                     else -> DEFAULT_DURATION
                 }.inWholeMilliseconds
         }
@@ -181,5 +182,6 @@
         val TO_AOD_DURATION = 1300.milliseconds
         val TO_DOZING_DURATION = 933.milliseconds
         val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
+        val TO_GLANCEABLE_HUB_DURATION = DEFAULT_DURATION
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 8cab3cd..35a2d58 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -24,16 +24,19 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
 import java.util.UUID
@@ -59,7 +62,6 @@
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
     keyguardInteractor: KeyguardInteractor,
-    private val flags: FeatureFlags,
     private val shadeRepository: ShadeRepository,
     powerInteractor: PowerInteractor,
     private val glanceableHubTransitions: GlanceableHubTransitions,
@@ -97,14 +99,13 @@
      * LOCKSCREEN is running.
      */
     val surfaceBehindVisibility: Flow<Boolean?> =
-        transitionInteractor.startedKeyguardTransitionStep
-            .map { startedStep ->
-                if (startedStep.to != KeyguardState.GONE) {
-                    // LOCKSCREEN to anything but GONE does not require any special surface
-                    // visibility handling.
-                    return@map null
-                }
-
+        transitionInteractor
+            .transition(
+                edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = Scenes.Gone),
+                edgeWithoutSceneContainer =
+                    Edge.create(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
+            )
+            .map<TransitionStep, Boolean?> {
                 true // Make the surface visible during LS -> GONE transitions.
             }
             .onStart {
@@ -368,7 +369,12 @@
                     // being delayed in KeyguardViewMediator
                     KeyguardState.DREAMING -> TO_DREAMING_DURATION + 100.milliseconds
                     KeyguardState.OCCLUDED -> TO_OCCLUDED_DURATION
-                    KeyguardState.AOD -> TO_AOD_DURATION
+                    KeyguardState.AOD ->
+                        if (powerInteractor.detailedWakefulness.value.lastSleepReason == FOLD) {
+                            TO_AOD_FOLD_DURATION
+                        } else {
+                            TO_AOD_DURATION
+                        }
                     KeyguardState.DOZING -> TO_DOZING_DURATION
                     KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> TO_DREAMING_HOSTED_DURATION
                     KeyguardState.GLANCEABLE_HUB -> TO_GLANCEABLE_HUB_DURATION
@@ -385,6 +391,7 @@
         val TO_DREAMING_HOSTED_DURATION = 933.milliseconds
         val TO_OCCLUDED_DURATION = 450.milliseconds
         val TO_AOD_DURATION = 500.milliseconds
+        val TO_AOD_FOLD_DURATION = 1100.milliseconds
         val TO_PRIMARY_BOUNCER_DURATION = DEFAULT_DURATION
         val TO_GONE_DURATION = 633.milliseconds
         val TO_GLANCEABLE_HUB_DURATION = 1.seconds
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 8cf4b53..f8208b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -22,11 +22,12 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -38,8 +39,9 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
 
@@ -54,7 +56,6 @@
     @Main mainDispatcher: CoroutineDispatcher,
     keyguardInteractor: KeyguardInteractor,
     private val communalInteractor: CommunalInteractor,
-    private val flags: FeatureFlags,
     private val keyguardSecurityModel: KeyguardSecurityModel,
     private val selectedUserInteractor: SelectedUserInteractor,
     powerInteractor: PowerInteractor,
@@ -79,21 +80,25 @@
     }
 
     val surfaceBehindVisibility: Flow<Boolean?> =
-        combine(
-                transitionInteractor.startedKeyguardTransitionStep,
-                transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER)
-            ) { startedStep, fromBouncerStep ->
-                if (startedStep.to != KeyguardState.GONE) {
-                    return@combine null
+        if (SceneContainerFlag.isEnabled) {
+            // The edge Scenes.Bouncer <-> Scenes.Gone is handled by STL
+            flowOf(null)
+        } else {
+            transitionInteractor
+                .transition(
+                    edge = Edge.INVALID,
+                    edgeWithoutSceneContainer =
+                        Edge.create(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE)
+                )
+                .map<TransitionStep, Boolean?> {
+                    it.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
                 }
-
-                fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
-            }
-            .onStart {
-                // Default to null ("don't care, use a reasonable default").
-                emit(null)
-            }
-            .distinctUntilChanged()
+                .onStart {
+                    // Default to null ("don't care, use a reasonable default").
+                    emit(null)
+                }
+                .distinctUntilChanged()
+        }
 
     fun dismissPrimaryBouncer() {
         scope.launch { startTransitionTo(KeyguardState.GONE) }
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 73835a3c..ab1194e 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
@@ -51,6 +51,7 @@
 import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
 import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
@@ -77,6 +78,7 @@
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transform
 
 /**
  * Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -91,7 +93,7 @@
     bouncerRepository: KeyguardBouncerRepository,
     configurationInteractor: ConfigurationInteractor,
     shadeRepository: ShadeRepository,
-    keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     sceneInteractorProvider: Provider<SceneInteractor>,
     private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>,
     private val fromLockscreenTransitionInteractor: Provider<FromLockscreenTransitionInteractor>,
@@ -110,7 +112,7 @@
                 keyguardTransitionInteractor.transitionState.map { step ->
                     val startingProgress = lastChangeStep.value
                     val progress = step.value
-                    if (step.to == AOD && progress >= startingProgress) {
+                    if (step.to == AOD && progress >= startingProgress && startingProgress < 1f) {
                         val adjustedProgress =
                             ((progress - startingProgress) / (1F - startingProgress)).coerceIn(
                                 0F,
@@ -248,21 +250,17 @@
     val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
 
     /** Keyguard can be clipped at the top as the shade is dragged */
-    val topClippingBounds: Flow<Int?> =
-        combineTransform(
-                configurationInteractor.onAnyConfigurationChange,
+    val topClippingBounds: Flow<Int?> by lazy {
+        repository.topClippingBounds
+            .sampleFilter(
                 keyguardTransitionInteractor
-                    .transitionValue(GONE)
-                    .map { it == 1f }
-                    .onStart { emit(false) }
-                    .distinctUntilChanged(),
-                repository.topClippingBounds
-            ) { _, isGone, topClippingBounds ->
-                if (!isGone) {
-                    emit(topClippingBounds)
-                }
+                    .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
+                    .onStart { emit(0f) }
+            ) { goneValue ->
+                goneValue != 1f
             }
             .distinctUntilChanged()
+    }
 
     /** Last point that [KeyguardRootView] view was tapped */
     val lastRootViewTapPosition: Flow<Point?> = repository.lastRootViewTapPosition.asStateFlow()
@@ -323,6 +321,10 @@
     @Deprecated("Use the relevant TransitionViewModel")
     val keyguardAlpha: Flow<Float> = repository.keyguardAlpha
 
+    /** Temporary shim for fading out content when the brightness slider is used */
+    @Deprecated("SceneContainer uses NotificationStackAppearanceInteractor")
+    val panelAlpha: StateFlow<Float> = repository.panelAlpha.asStateFlow()
+
     /**
      * When the lockscreen can be dismissed, emit an alpha value as the user swipes up. This is
      * useful just before the code commits to moving to GONE.
@@ -330,28 +332,35 @@
      * This uses legacyShadeExpansion to process swipe up events. In the future, the touch input
      * signal should be sent directly to transitions.
      */
-    val dismissAlpha: Flow<Float?> =
+    val dismissAlpha: Flow<Float> =
         shadeRepository.legacyShadeExpansion
-            .filter { it < 1f }
             .sampleCombine(
                 statusBarState,
                 keyguardTransitionInteractor.currentKeyguardState,
+                keyguardTransitionInteractor.transitionState,
                 isKeyguardDismissible,
             )
-            .map {
-                (legacyShadeExpansion, statusBarState, currentKeyguardState, isKeyguardDismissible)
-                ->
+            .filter { (_, _, _, step, _) -> !step.transitionState.isTransitioning() }
+            .transform {
+                (
+                    legacyShadeExpansion,
+                    statusBarState,
+                    currentKeyguardState,
+                    step,
+                    isKeyguardDismissible) ->
                 if (
                     statusBarState == StatusBarState.KEYGUARD &&
                         isKeyguardDismissible &&
-                        currentKeyguardState == LOCKSCREEN
+                        currentKeyguardState == LOCKSCREEN &&
+                        legacyShadeExpansion != 1f
                 ) {
-                    MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion)
-                } else {
-                    null
+                    emit(MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion))
+                } else if (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f) {
+                    // Resets alpha state
+                    emit(1f)
                 }
             }
-            .onStart { emit(null) }
+            .onStart { emit(1f) }
             .distinctUntilChanged()
 
     val keyguardTranslationY: Flow<Float> =
@@ -453,6 +462,10 @@
         repository.setKeyguardAlpha(alpha)
     }
 
+    fun setPanelAlpha(alpha: Float) {
+        repository.setPanelAlpha(alpha)
+    }
+
     fun setAnimateDozingTransitions(animate: Boolean) {
         repository.setAnimateDozingTransitions(animate)
     }
@@ -477,6 +490,10 @@
         repository.topClippingBounds.value = top
     }
 
+    fun setDreaming(isDreaming: Boolean) {
+        repository.setDreaming(isDreaming)
+    }
+
     /** Temporary shim, until [KeyguardWmStateRefactor] is enabled */
     fun showKeyguard() {
         fromGoneTransitionInteractor.get().showKeyguard()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 2766b71..37272dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -223,6 +223,17 @@
         }
     }
 
+    fun transitionValue(
+        scene: SceneKey,
+        stateWithoutSceneContainer: KeyguardState,
+    ): Flow<Float> {
+        return if (SceneContainerFlag.isEnabled) {
+            sceneInteractor.get().transitionProgress(scene)
+        } else {
+            transitionValue(stateWithoutSceneContainer)
+        }
+    }
+
     /**
      * The amount of transition into or out of the given [KeyguardState].
      *
@@ -232,26 +243,13 @@
     fun transitionValue(
         state: KeyguardState,
     ): Flow<Float> {
+        if (SceneContainerFlag.isEnabled && state != state.mapToSceneContainerState()) {
+            Log.e(TAG, "SceneContainer is enabled but a deprecated state $state is used.")
+            return transitionValue(state.mapToSceneContainerScene()!!, state)
+        }
         return getTransitionValueFlow(state)
     }
 
-    /**
-     * AOD<->* transition information, mapped to dozeAmount range of AOD (1f) <->
-     * * (0f).
-     */
-    @SuppressLint("SharedFlowCreation")
-    val dozeAmountTransition: Flow<TransitionStep> =
-        repository.transitions
-            .filter { step -> step.from == AOD || step.to == AOD }
-            .map { step ->
-                if (step.from == AOD) {
-                    step.copy(value = 1 - step.value)
-                } else {
-                    step
-                }
-            }
-            .shareIn(scope, SharingStarted.Eagerly, replay = 1)
-
     /** The last [TransitionStep] with a [TransitionState] of STARTED */
     val startedKeyguardTransitionStep: Flow<TransitionStep> =
         repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
@@ -267,8 +265,6 @@
             .map { step -> step.to }
             .shareIn(scope, SharingStarted.Eagerly, replay = 1)
 
-    val currentTransitionInfo: StateFlow<TransitionInfo> = repository.currentTransitionInfoInternal
-
     /** The from state of the last [TransitionState.STARTED] transition. */
     // TODO: is it performant to have several SharedFlows side by side instead of one?
     @SuppressLint("SharedFlowCreation")
@@ -415,14 +411,6 @@
     /** Whether we've currently STARTED a transition and haven't yet FINISHED it. */
     val isInTransitionToAnyState = isInTransitionWhere({ true }, { true })
 
-    fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> {
-        return transition(Edge.create(from = fromState, to = null))
-    }
-
-    fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> {
-        return transition(Edge.create(from = null, to = toState))
-    }
-
     /**
      * Called to start a transition that will ultimately dismiss the keyguard from the current
      * state.
@@ -558,10 +546,6 @@
         return currentKeyguardState.replayCache.last()
     }
 
-    fun getStartedState(): KeyguardState {
-        return startedKeyguardState.replayCache.last()
-    }
-
     fun getStartedFromState(): KeyguardState {
         return startedKeyguardFromState.replayCache.last()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
index 6729246..21b9e53 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/ToAodFoldTransitionInteractor.kt
@@ -16,30 +16,17 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import android.animation.ValueAnimator
 import android.view.ViewGroup
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.shade.NotificationPanelViewController
 import com.android.systemui.shade.ShadeFoldAnimator
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
 
 @SysUISingleton
 class ToAodFoldTransitionInteractor
 @Inject
 constructor(
     private val keyguardClockInteractor: KeyguardClockInteractor,
-    private val transitionInteractor: KeyguardTransitionInteractor,
-    private val transitionRepository: KeyguardTransitionRepository,
-    @Application private val mainScope: CoroutineScope,
-    @Main private val mainDispatcher: CoroutineDispatcher,
 ) {
     private var parentAnimator: NotificationPanelViewController.ShadeFoldAnimatorImpl? = null
 
@@ -50,7 +37,6 @@
                 get() = throw NotImplementedError("Deprecated. Do not call.")
 
             override fun prepareFoldToAodAnimation() {
-                forceToAod()
                 parentAnimator?.prepareFoldToAodAnimation()
             }
 
@@ -78,21 +64,6 @@
             parentAnimator as? NotificationPanelViewController.ShadeFoldAnimatorImpl?
     }
 
-    /** Forces the keyguard into AOD or Doze */
-    private fun forceToAod() {
-        mainScope.launch(mainDispatcher) {
-            transitionRepository.startTransition(
-                TransitionInfo(
-                    "$TAG (Fold transition triggered)",
-                    transitionInteractor.getCurrentState(),
-                    transitionInteractor.asleepKeyguardState.value,
-                    ValueAnimator().apply { duration = 0 },
-                    TransitionModeOnCanceled.LAST_VALUE,
-                )
-            )
-        }
-    }
-
     companion object {
         private val TAG = ToAodFoldTransitionInteractor::class.simpleName!!
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 88e6602..069f65b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -105,7 +105,17 @@
                         is ObservableTransitionState.Transition ->
                             when {
                                 transitionState.fromScene == Scenes.Lockscreen &&
-                                    transitionState.toScene == Scenes.Gone -> flowOf(true)
+                                    transitionState.toScene == Scenes.Gone ->
+                                    sceneInteractor
+                                        .get()
+                                        .isTransitionUserInputOngoing
+                                        .flatMapLatestConflated { isUserInputOngoing ->
+                                            if (isUserInputOngoing) {
+                                                isDeviceEntered
+                                            } else {
+                                                flowOf(true)
+                                            }
+                                        }
                                 transitionState.fromScene == Scenes.Bouncer &&
                                     transitionState.toScene == Scenes.Gone ->
                                     transitionState.progress.map { progress ->
@@ -229,11 +239,14 @@
     val aodVisibility: Flow<Boolean> =
         combine(
                 keyguardInteractor.isDozing,
+                keyguardInteractor.isAodAvailable,
                 keyguardInteractor.biometricUnlockState,
-            ) { isDozing, biometricUnlockState ->
+            ) { isDozing, isAodAvailable, biometricUnlockState ->
                 // AOD is visible if we're dozing, unless we are wake and unlocking (where we go
                 // directly from AOD to unlocked while dozing).
-                isDozing && !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
+                isDozing &&
+                    isAodAvailable &&
+                    !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
             }
             .distinctUntilChanged()
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index 3baeb76..9b3ba7d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -131,7 +131,7 @@
         val newTransition =
             TransitionInfo(
                 ownerName = this::class.java.simpleName,
-                from = transitionInteractor.currentTransitionInfo.value.to,
+                from = transitionInteractor.currentTransitionInfoInternal.value.to,
                 to = state,
                 animator = null,
                 modeOnCanceled = TransitionModeOnCanceled.REVERSE
@@ -150,7 +150,7 @@
     private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
         if (transition.fromScene == Scenes.Lockscreen) {
             if (currentTransitionId != null) {
-                val currentToState = transitionInteractor.currentTransitionInfo.value.to
+                val currentToState = transitionInteractor.currentTransitionInfoInternal.value.to
                 if (currentToState == UNDEFINED) {
                     transitionKtfTo(transitionInteractor.getStartedFromState())
                 }
@@ -201,7 +201,7 @@
     }
 
     private suspend fun startTransitionFromLockscreen() {
-        val currentState = transitionInteractor.currentTransitionInfo.value.to
+        val currentState = transitionInteractor.currentTransitionInfoInternal.value.to
         val newTransition =
             TransitionInfo(
                 ownerName = this::class.java.simpleName,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
index c1e8d22..1306b26 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
@@ -96,10 +96,23 @@
     companion object {
         private const val TAG = "Edge"
 
+        @JvmStatic
+        @JvmOverloads
         fun create(from: KeyguardState? = null, to: KeyguardState? = null) = StateToState(from, to)
 
+        @JvmStatic
+        @JvmOverloads
         fun create(from: KeyguardState? = null, to: SceneKey) = StateToScene(from, to)
 
+        @JvmStatic
+        @JvmOverloads
         fun create(from: SceneKey, to: KeyguardState? = null) = SceneToState(from, to)
+
+        /**
+         * This edge is a placeholder for when an edge needs to be passed but there is no edge for
+         * this flag configuration available. Usually for Scene <-> Scene edges with scene container
+         * enabled where these edges are managed by STL separately.
+         */
+        val INVALID = StateToState(UNDEFINED, UNDEFINED)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 2b4c4af..0a8c190 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -15,6 +15,9 @@
  */
 package com.android.systemui.keyguard.shared.model
 
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+
 /** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */
 data class TransitionStep
 @JvmOverloads
@@ -39,3 +42,6 @@
         return to == state && transitionState == TransitionState.FINISHED
     }
 }
+
+fun Flow<TransitionStep>.filterState(transitionState: TransitionState) =
+    this.filter { it.transitionState == transitionState }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
index 6b11dc5..191056c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt
@@ -121,10 +121,7 @@
             ) {
                 val dateView =
                     constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
-                val weatherView =
-                    constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
                 addView(dateView)
-                addView(weatherView)
             }
         }
     }
@@ -141,9 +138,6 @@
             ) {
                 val dateView =
                     constraintLayout.requireViewById<View>(sharedR.id.date_smartspace_view)
-                val weatherView =
-                    constraintLayout.requireViewById<View>(sharedR.id.weather_smartspace_view)
-                removeView(weatherView)
                 removeView(dateView)
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index 1f4bc61..ecdc21c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GoneToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDozingTransitionViewModel
@@ -246,4 +247,10 @@
     abstract fun occludedToGlanceableHub(
         impl: OccludedToGlanceableHubTransitionViewModel
     ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
+    abstract fun goneToGlanceableHub(
+        impl: GoneToGlanceableHubTransitionViewModel
+    ): DeviceEntryIconTransition
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 7a2e610..215ac46 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -63,10 +63,10 @@
     private fun setupAccessibilityDelegate() {
         accessibilityDelegate =
             object : AccessibilityDelegate() {
-                private val accessibilityAuthenticateHint =
+                private val accessibilityBouncerHint =
                     AccessibilityNodeInfo.AccessibilityAction(
                         AccessibilityNodeInfoCompat.ACTION_CLICK,
-                        resources.getString(R.string.accessibility_authenticate_hint)
+                        resources.getString(R.string.accessibility_bouncer)
                     )
                 private val accessibilityEnterHint =
                     AccessibilityNodeInfo.AccessibilityAction(
@@ -79,8 +79,8 @@
                 ) {
                     super.onInitializeAccessibilityNodeInfo(v, info)
                     when (accessibilityHintType) {
-                        AccessibilityHintType.AUTHENTICATE ->
-                            info.addAction(accessibilityAuthenticateHint)
+                        AccessibilityHintType.BOUNCER ->
+                            info.addAction(accessibilityBouncerHint)
                         AccessibilityHintType.ENTER -> info.addAction(accessibilityEnterHint)
                         AccessibilityHintType.NONE -> return
                     }
@@ -274,7 +274,7 @@
 
     enum class AccessibilityHintType {
         NONE,
-        AUTHENTICATE,
+        BOUNCER,
         ENTER,
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
index 39f1ebe..aa0a9d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
@@ -62,7 +62,7 @@
                 addTransition(
                     clockViewModel.currentClock.value?.let { DefaultClockSteppingTransition(it) }
                 )
-            else -> addTransition(ClockSizeTransition(config, clockViewModel, smartspaceViewModel))
+            else -> addTransition(ClockSizeTransition(config, clockViewModel))
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 0435531..8a751f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.view.View
+import android.view.ViewGroup
 import android.view.ViewTreeObserver.OnGlobalLayoutListener
 import androidx.constraintlayout.widget.Barrier
 import androidx.constraintlayout.widget.ConstraintLayout
@@ -51,7 +52,7 @@
 ) : KeyguardSection() {
     private var smartspaceView: View? = null
     private var weatherView: View? = null
-    private var dateView: View? = null
+    private var dateWeatherView: ViewGroup? = null
 
     private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null
     private var pastVisibility: Int = -1
@@ -69,12 +70,15 @@
         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
         smartspaceView = smartspaceController.buildAndConnectView(constraintLayout)
         weatherView = smartspaceController.buildAndConnectWeatherView(constraintLayout)
-        dateView = smartspaceController.buildAndConnectDateView(constraintLayout)
+        dateWeatherView =
+            smartspaceController.buildAndConnectDateView(constraintLayout) as ViewGroup
         pastVisibility = smartspaceView?.visibility ?: View.GONE
         constraintLayout.addView(smartspaceView)
         if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
-            constraintLayout.addView(weatherView)
-            constraintLayout.addView(dateView)
+            constraintLayout.addView(dateWeatherView)
+            // Place weather right after the date, before the extras (alarm and dnd)
+            val index = if (dateWeatherView?.childCount == 0) 0 else 1
+            dateWeatherView?.addView(weatherView, index)
         }
         keyguardUnlockAnimationController.lockscreenSmartspace = smartspaceView
         smartspaceVisibilityListener = OnGlobalLayoutListener {
@@ -116,26 +120,6 @@
                 ConstraintSet.START,
                 horizontalPaddingStart
             )
-            constrainWidth(sharedR.id.weather_smartspace_view, ConstraintSet.WRAP_CONTENT)
-            connect(
-                sharedR.id.weather_smartspace_view,
-                ConstraintSet.TOP,
-                sharedR.id.date_smartspace_view,
-                ConstraintSet.TOP
-            )
-            connect(
-                sharedR.id.weather_smartspace_view,
-                ConstraintSet.BOTTOM,
-                sharedR.id.date_smartspace_view,
-                ConstraintSet.BOTTOM
-            )
-            connect(
-                sharedR.id.weather_smartspace_view,
-                ConstraintSet.START,
-                sharedR.id.date_smartspace_view,
-                ConstraintSet.END,
-                4
-            )
 
             // migrate addSmartspaceView from KeyguardClockSwitchController
             constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT)
@@ -186,7 +170,6 @@
                 *intArrayOf(
                     sharedR.id.bc_smartspace_view,
                     sharedR.id.date_smartspace_view,
-                    sharedR.id.weather_smartspace_view,
                 )
             )
         }
@@ -196,7 +179,7 @@
     override fun removeViews(constraintLayout: ConstraintLayout) {
         if (!MigrateClocksToBlueprint.isEnabled) return
         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
-        listOf(smartspaceView, dateView, weatherView).forEach {
+        listOf(smartspaceView, dateWeatherView).forEach {
             it?.let {
                 if (it.parent == constraintLayout) {
                     constraintLayout.removeView(it)
@@ -220,7 +203,7 @@
             setVisibility(sharedR.id.weather_smartspace_view, weatherVisibility)
             setAlpha(
                 sharedR.id.weather_smartspace_view,
-                if (weatherVisibility == ConstraintSet.VISIBLE) 1f else 0f
+                if (weatherVisibility == View.VISIBLE) 1f else 0f
             )
             val dateVisibility =
                 if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) ConstraintSet.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index f17dbd2..4d914c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -31,8 +31,9 @@
 import com.android.app.animation.Interpolators
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
+import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS
+import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_UP_MILLIS
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
 import com.android.systemui.res.R
 import com.android.systemui.shared.R as sharedR
 import com.google.android.material.math.MathUtils
@@ -46,13 +47,12 @@
 class ClockSizeTransition(
     config: IntraBlueprintTransition.Config,
     clockViewModel: KeyguardClockViewModel,
-    smartspaceViewModel: KeyguardSmartspaceViewModel,
 ) : TransitionSet() {
     init {
         ordering = ORDERING_TOGETHER
         if (config.type != Type.SmartspaceVisibility) {
-            addTransition(ClockFaceOutTransition(config, clockViewModel, smartspaceViewModel))
-            addTransition(ClockFaceInTransition(config, clockViewModel, smartspaceViewModel))
+            addTransition(ClockFaceOutTransition(config, clockViewModel))
+            addTransition(ClockFaceInTransition(config, clockViewModel))
         }
         addTransition(SmartspaceMoveTransition(config, clockViewModel))
     }
@@ -60,8 +60,11 @@
     abstract class VisibilityBoundsTransition() : Transition() {
         abstract val captureSmartspace: Boolean
         protected val TAG = this::class.simpleName!!
+
         override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
+
         override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
+
         override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
 
         private fun captureValues(transition: TransitionValues) {
@@ -222,21 +225,19 @@
         }
     }
 
-    class ClockFaceInTransition(
+    abstract class ClockFaceTransition(
         config: IntraBlueprintTransition.Config,
         val viewModel: KeyguardClockViewModel,
-        val smartspaceViewModel: KeyguardSmartspaceViewModel,
     ) : VisibilityBoundsTransition() {
-        override val captureSmartspace = !viewModel.isLargeClockVisible.value
+        protected abstract val isLargeClock: Boolean
+        protected abstract val smallClockMoveScale: Float
+        override val captureSmartspace
+            get() = !isLargeClock
 
-        init {
-            duration = CLOCK_IN_MILLIS
-            startDelay = CLOCK_IN_START_DELAY_MILLIS
-            interpolator = CLOCK_IN_INTERPOLATOR
-
-            if (viewModel.isLargeClockVisible.value) {
+        protected fun addTargets() {
+            if (isLargeClock) {
                 viewModel.currentClock.value?.let {
-                    if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}")
+                    if (DEBUG) Log.i(TAG, "Adding large clock views: ${it.largeClock.layout.views}")
                     it.largeClock.layout.views.forEach { addTarget(it) }
                 }
                     ?: run {
@@ -244,7 +245,7 @@
                         addTarget(R.id.lockscreen_clock_view_large)
                     }
             } else {
-                if (DEBUG) Log.i(TAG, "Small Clock In")
+                if (DEBUG) Log.i(TAG, "Adding small clock")
                 addTarget(R.id.lockscreen_clock_view)
             }
         }
@@ -262,89 +263,59 @@
             if (fromIsVis == toIsVis) return
 
             fromBounds.set(toBounds)
-            if (viewModel.isLargeClockVisible.value) {
+            if (isLargeClock) {
                 // Large clock shouldn't move; fromBounds already set
             } else if (toSSBounds != null && fromSSBounds != null) {
                 // Instead of moving the small clock the full distance, we compute the distance
                 // smartspace will move. We then scale this to match the duration of this animation
                 // so that the small clock moves at the same speed as smartspace.
                 val ssTranslation =
-                    abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_IN_MOVE_SCALE).toInt()
+                    abs((toSSBounds.top - fromSSBounds.top) * smallClockMoveScale).toInt()
                 fromBounds.top = toBounds.top - ssTranslation
                 fromBounds.bottom = toBounds.bottom - ssTranslation
             } else {
                 Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
             }
         }
+    }
+
+    class ClockFaceInTransition(
+        config: IntraBlueprintTransition.Config,
+        viewModel: KeyguardClockViewModel,
+    ) : ClockFaceTransition(config, viewModel) {
+        override val isLargeClock = viewModel.isLargeClockVisible.value
+        override val smallClockMoveScale = CLOCK_IN_MILLIS / STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
+
+        init {
+            duration = CLOCK_IN_MILLIS
+            startDelay = CLOCK_IN_START_DELAY_MILLIS
+            interpolator = CLOCK_IN_INTERPOLATOR
+            addTargets()
+        }
 
         companion object {
             const val CLOCK_IN_MILLIS = 167L
             const val CLOCK_IN_START_DELAY_MILLIS = 133L
             val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN
-            const val SMALL_CLOCK_IN_MOVE_SCALE =
-                CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
         }
     }
 
     class ClockFaceOutTransition(
         config: IntraBlueprintTransition.Config,
-        val viewModel: KeyguardClockViewModel,
-        val smartspaceViewModel: KeyguardSmartspaceViewModel,
-    ) : VisibilityBoundsTransition() {
-        override val captureSmartspace = viewModel.isLargeClockVisible.value
+        viewModel: KeyguardClockViewModel,
+    ) : ClockFaceTransition(config, viewModel) {
+        override val isLargeClock = !viewModel.isLargeClockVisible.value
+        override val smallClockMoveScale = CLOCK_OUT_MILLIS / STATUS_AREA_MOVE_UP_MILLIS.toFloat()
 
         init {
             duration = CLOCK_OUT_MILLIS
             interpolator = CLOCK_OUT_INTERPOLATOR
-
-            if (viewModel.isLargeClockVisible.value) {
-                if (DEBUG) Log.i(TAG, "Small Clock Out")
-                addTarget(R.id.lockscreen_clock_view)
-            } else {
-                viewModel.currentClock.value?.let {
-                    if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}")
-                    it.largeClock.layout.views.forEach { addTarget(it) }
-                }
-                    ?: run {
-                        Log.e(TAG, "No large clock set, falling back")
-                        addTarget(R.id.lockscreen_clock_view_large)
-                    }
-            }
-        }
-
-        override fun mutateBounds(
-            view: View,
-            fromIsVis: Boolean,
-            toIsVis: Boolean,
-            fromBounds: Rect,
-            toBounds: Rect,
-            fromSSBounds: Rect?,
-            toSSBounds: Rect?
-        ) {
-            // Move normally if clock is not changing visibility
-            if (fromIsVis == toIsVis) return
-
-            toBounds.set(fromBounds)
-            if (!viewModel.isLargeClockVisible.value) {
-                // Large clock shouldn't move; toBounds already set
-            } else if (toSSBounds != null && fromSSBounds != null) {
-                // Instead of moving the small clock the full distance, we compute the distance
-                // smartspace will move. We then scale this to match the duration of this animation
-                // so that the small clock moves at the same speed as smartspace.
-                val ssTranslation =
-                    abs((toSSBounds.top - fromSSBounds.top) * SMALL_CLOCK_OUT_MOVE_SCALE).toInt()
-                toBounds.top = fromBounds.top - ssTranslation
-                toBounds.bottom = fromBounds.bottom - ssTranslation
-            } else {
-                Log.e(TAG, "mutateBounds: smallClock received no smartspace bounds")
-            }
+            addTargets()
         }
 
         companion object {
             const val CLOCK_OUT_MILLIS = 133L
             val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR
-            const val SMALL_CLOCK_OUT_MOVE_SCALE =
-                CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat()
         }
     }
 
@@ -353,15 +324,14 @@
         val config: IntraBlueprintTransition.Config,
         viewModel: KeyguardClockViewModel,
     ) : VisibilityBoundsTransition() {
+        private val isLargeClock = viewModel.isLargeClockVisible.value
         override val captureSmartspace = false
 
         init {
             duration =
-                if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS
-                else STATUS_AREA_MOVE_DOWN_MILLIS
+                if (isLargeClock) STATUS_AREA_MOVE_UP_MILLIS else STATUS_AREA_MOVE_DOWN_MILLIS
             interpolator = Interpolators.EMPHASIZED
             addTarget(sharedR.id.date_smartspace_view)
-            addTarget(sharedR.id.weather_smartspace_view)
             addTarget(sharedR.id.bc_smartspace_view)
 
             // Notifications normally and media on split shade needs to be moved
@@ -391,6 +361,6 @@
     }
 
     companion object {
-        val DEBUG = true
+        val DEBUG = false
     }
 }
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 c05a1b7..62b4782 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
@@ -29,6 +29,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
 import com.android.systemui.keyguard.shared.model.ClockSize
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.res.R
 import javax.inject.Inject
@@ -54,6 +55,7 @@
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+    private val lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
     private val aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
     private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
     private val keyguardClockViewModel: KeyguardClockViewModel,
@@ -73,13 +75,30 @@
                 burnInParams
             }
         return configurationInteractor
-            .dimensionPixelSize(R.dimen.keyguard_enter_from_top_translation_y)
-            .flatMapLatest { enterFromTopAmount ->
+            .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(enterFromTopAmount)
+                        .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)
@@ -87,21 +106,31 @@
                     aodToLockscreenTransitionViewModel.translationY(params.translationY).onStart {
                         emit(StateToValue())
                     },
-                ) {
-                    keyguardTranslationY,
-                    burnInModel,
-                    goneToAod,
-                    occludedToLockscreen,
-                    aodToLockscreen ->
+                ) { 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 (goneToAod.transitionState.isTransitioning()) {
-                            (goneToAod.value ?: 0f) + burnInModel.translationY
+                        } else if (goneToAodTranslationY.transitionState.isTransitioning()) {
+                            (goneToAodTranslationY.value ?: 0f) + burnInModel.translationY
                         } else {
                             burnInModel.translationY + occludedToLockscreen + keyguardTranslationY
                         }
-                    burnInModel.copy(translationY = translationY.toInt())
+                    val translationX =
+                        burnInModel.translationX +
+                            (goneToAodTranslationX.value ?: 0f) +
+                            (lockscreenToAodTranslationX.value ?: 0f)
+                    burnInModel.copy(
+                        translationX = translationX.toInt(),
+                        translationY = translationY.toInt(),
+                    )
                 }
             }
             .distinctUntilChanged()
@@ -111,8 +140,8 @@
         params: BurnInParameters,
     ): Flow<BurnInModel> {
         return combine(
-            keyguardTransitionInteractor.dozeAmountTransition.map {
-                Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
+            keyguardTransitionInteractor.transitionValue(KeyguardState.AOD).map {
+                Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it)
             },
             burnInInteractor.burnIn(
                 xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 0f1f5c1..06b76b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -52,8 +52,11 @@
     udfpsOverlayInteractor: UdfpsOverlayInteractor,
 ) {
     private val isShowingAodOrDozing: Flow<Boolean> =
-        transitionInteractor.startedKeyguardState.map { keyguardState ->
-            keyguardState == KeyguardState.AOD || keyguardState == KeyguardState.DOZING
+        combine(
+            transitionInteractor.startedKeyguardState,
+            transitionInteractor.transitionValue(KeyguardState.DOZING),
+        ) { startedKeyguardState, dozingTransitionValue ->
+            startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f
         }
 
     private fun getColor(usingBackgroundProtection: Boolean): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index fa43ec2..4688088 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -162,6 +162,7 @@
             KeyguardState.LOCKSCREEN, -> 1f
         }
     }
+
     val useBackgroundProtection: StateFlow<Boolean> = isUdfpsSupported
     val burnInOffsets: Flow<BurnInOffsets> =
         deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled
@@ -263,13 +264,7 @@
     val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
         accessibilityInteractor.isEnabled.flatMapLatest { touchExplorationEnabled ->
             if (touchExplorationEnabled) {
-                combine(iconType, isInteractive) { iconType, isInteractive ->
-                    if (isInteractive) {
-                        iconType.toAccessibilityHintType()
-                    } else {
-                        DeviceEntryIconView.AccessibilityHintType.NONE
-                    }
-                }
+                iconType.map { it.toAccessibilityHintType() }
             } else {
                 flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
             }
@@ -289,8 +284,7 @@
     private fun DeviceEntryIconView.IconType.toAccessibilityHintType():
         DeviceEntryIconView.AccessibilityHintType {
         return when (this) {
-            DeviceEntryIconView.IconType.LOCK ->
-                DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE
+            DeviceEntryIconView.IconType.LOCK -> DeviceEntryIconView.AccessibilityHintType.BOUNCER
             DeviceEntryIconView.IconType.UNLOCK -> DeviceEntryIconView.AccessibilityHintType.ENTER
             DeviceEntryIconView.IconType.FINGERPRINT,
             DeviceEntryIconView.IconType.NONE -> DeviceEntryIconView.AccessibilityHintType.NONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
index 77ebfce..480f948 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
-import android.util.MathUtils
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GONE_DURATION
 import com.android.systemui.keyguard.shared.model.Edge
@@ -50,12 +49,10 @@
             )
 
     fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
-        var startAlpha = 1f
         return transitionAnimation.sharedFlow(
             duration = 200.milliseconds,
-            onStart = { startAlpha = viewState.alpha() },
-            onStep = { MathUtils.lerp(startAlpha, 0f, it) },
-            onFinish = { 0f },
+            onStart = { 0f },
+            onStep = { 0f },
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 74f7d75..2bc8e51 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -26,13 +26,17 @@
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.transform
 
 /** Breaks down GONE->AOD transition into discrete steps for corresponding views to consume. */
 @ExperimentalCoroutinesApi
@@ -41,6 +45,7 @@
 @Inject
 constructor(
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+    private val powerInteractor: PowerInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
 
@@ -56,13 +61,38 @@
 
     /** y-translation from the top of the screen for AOD */
     fun enterFromTopTranslationY(translatePx: Int): Flow<StateToValue> {
-        return transitionAnimation.sharedFlowWithState(
-            startTime = 600.milliseconds,
-            duration = 500.milliseconds,
-            onStep = { translatePx + it * -translatePx },
-            onFinish = { 0f },
-            interpolator = EMPHASIZED_DECELERATE,
-        )
+        return transitionAnimation
+            .sharedFlowWithState(
+                startTime = 600.milliseconds,
+                duration = 500.milliseconds,
+                onStep = { translatePx + it * -translatePx },
+                onFinish = { 0f },
+                interpolator = EMPHASIZED_DECELERATE,
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (stateToValue, wakefulness) ->
+                if (wakefulness.lastSleepReason != FOLD) {
+                    emit(stateToValue)
+                }
+            }
+    }
+
+    /** x-translation from the side of the screen for fold animation */
+    fun enterFromSideTranslationX(translatePx: Int): Flow<StateToValue> {
+        return transitionAnimation
+            .sharedFlowWithState(
+                startTime = 500.milliseconds,
+                duration = 600.milliseconds,
+                onStep = { translatePx + it * -translatePx },
+                onFinish = { 0f },
+                interpolator = EMPHASIZED_DECELERATE,
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (stateToValue, wakefulness) ->
+                if (wakefulness.lastSleepReason == FOLD) {
+                    emit(stateToValue)
+                }
+            }
     }
 
     val notificationAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
index 70c0032..c62e4f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
@@ -60,6 +60,14 @@
             onFinish = { 1f },
         )
 
+    val notificationAlpha: Flow<Float> =
+        transitionAnimation.sharedFlow(
+            duration = 500.milliseconds,
+            onStep = { 1f - it },
+            // Needs to be 1f in order for HUNs to appear on AOD
+            onFinish = { 1f },
+        )
+
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt
new file mode 100644
index 0000000..8eab406
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToGlanceableHubTransitionViewModel.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class GoneToGlanceableHubTransitionViewModel
+@Inject
+constructor(
+    animationFlow: KeyguardTransitionAnimationFlow,
+) : DeviceEntryIconTransition {
+
+    private val transitionAnimation =
+        animationFlow
+            .setup(duration = TO_GLANCEABLE_HUB_DURATION, edge = Edge.create(GONE, Scenes.Communal))
+            .setupWithoutSceneContainer(edge = Edge.create(GONE, GLANCEABLE_HUB))
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        transitionAnimation.sharedFlow(
+            duration = 167.milliseconds,
+            onStep = { it },
+            onCancel = { 0f },
+            onFinish = { 1f },
+        )
+}
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 7ac03bf..c6efcfa 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
@@ -130,6 +130,6 @@
 
     companion object {
         private const val TAG = "KeyguardBlueprintViewModel"
-        private const val DEBUG = true
+        private const val DEBUG = false
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 940f423..f5c521a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
 import com.android.systemui.statusbar.ui.SystemBarUtilsProxy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -48,6 +49,7 @@
 constructor(
     keyguardClockInteractor: KeyguardClockInteractor,
     @Application private val applicationScope: CoroutineScope,
+    aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
     notifsKeyguardInteractor: NotificationsKeyguardInteractor,
     @get:VisibleForTesting val shadeInteractor: ShadeInteractor,
     private val systemBarUtils: SystemBarUtilsProxy,
@@ -105,8 +107,13 @@
             initialValue = false
         )
 
-    val isAodIconsVisible: StateFlow<Boolean> =
-        notifsKeyguardInteractor.areNotificationsFullyHidden.stateIn(
+    // To translate elements below smartspace in weather clock to avoid overlapping between date
+    // element in weather clock and aod icons
+    val isAodIconsVisible: StateFlow<Boolean> = combine(aodNotificationIconViewModel.icons.map {
+        it.visibleIcons.isNotEmpty()
+    }, notifsKeyguardInteractor.areNotificationsFullyHidden) { hasIcons, visible ->
+        hasIcons && visible
+    }.stateIn(
             scope = applicationScope,
             started = SharingStarted.WhileSubscribed(),
             initialValue = false
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 ee52ad0..d7ac976 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
@@ -66,7 +66,6 @@
 import kotlinx.coroutines.flow.combineTransform
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onStart
@@ -203,7 +202,7 @@
         combine(
                 communalInteractor.isIdleOnCommunal,
                 keyguardTransitionInteractor
-                    .transitionValue(GONE)
+                    .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
                     .map { it == 1f }
                     .onStart { emit(false) },
                 keyguardTransitionInteractor
@@ -236,7 +235,7 @@
                 // value emitted by any of them. Do not add flows that cannot make this guarantee.
                 merge(
                         alphaOnShadeExpansion,
-                        keyguardInteractor.dismissAlpha.filterNotNull(),
+                        keyguardInteractor.dismissAlpha,
                         alternateBouncerToGoneTransitionViewModel.lockscreenAlpha(viewState),
                         aodToGoneTransitionViewModel.lockscreenAlpha(viewState),
                         aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
@@ -252,6 +251,7 @@
                         goneToDreamingTransitionViewModel.lockscreenAlpha,
                         goneToLockscreenTransitionViewModel.lockscreenAlpha,
                         lockscreenToAodTransitionViewModel.lockscreenAlpha(viewState),
+                        lockscreenToAodTransitionViewModel.lockscreenAlphaOnFold,
                         lockscreenToDozingTransitionViewModel.lockscreenAlpha,
                         lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
                         lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 02e48fc..10cfd6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -27,11 +27,13 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import com.android.systemui.util.kotlin.filterValuesNotNull
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -89,37 +91,36 @@
         isCommunalAvailable: Boolean,
         shadeMode: ShadeMode,
     ): Map<UserAction, UserActionResult> {
-        val shadeSceneKey =
+        val notifShadeSceneKey =
             UserActionResult(
-                toScene =
-                    if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
+                toScene = SceneFamilies.NotifShade,
                 transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
             )
 
-        val quickSettingsIfSingleShade =
-            if (shadeMode is ShadeMode.Single) UserActionResult(Scenes.QuickSettings)
-            else shadeSceneKey
-
         return mapOf(
                 Swipe.Left to UserActionResult(Scenes.Communal).takeIf { isCommunalAvailable },
                 Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer,
 
                 // Swiping down from the top edge goes to QS (or shade if in split shade mode).
-                swipeDownFromTop(pointerCount = 1) to quickSettingsIfSingleShade,
-                swipeDownFromTop(pointerCount = 2) to
-                    // TODO(b/338577208): Remove 'Dual' once we add Dual Shade invocation zones.
-                    if (shadeMode is ShadeMode.Dual) {
-                        UserActionResult(Scenes.QuickSettingsShade)
+                swipeDownFromTop(pointerCount = 1) to
+                    if (shadeMode is ShadeMode.Single) {
+                        UserActionResult(Scenes.QuickSettings)
                     } else {
-                        quickSettingsIfSingleShade
+                        notifShadeSceneKey
                     },
 
-                // Swiping down, not from the edge, always navigates to the shade scene.
-                swipeDown(pointerCount = 1) to shadeSceneKey,
-                swipeDown(pointerCount = 2) to shadeSceneKey,
+                // TODO(b/338577208): Remove once we add Dual Shade invocation zones.
+                swipeDownFromTop(pointerCount = 2) to
+                    UserActionResult(
+                        toScene = SceneFamilies.QuickSettings,
+                        transitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                    ),
+
+                // Swiping down, not from the edge, always navigates to the notif shade scene.
+                swipeDown(pointerCount = 1) to notifShadeSceneKey,
+                swipeDown(pointerCount = 2) to notifShadeSceneKey,
             )
-            .filterValues { it != null }
-            .mapValues { checkNotNull(it.value) }
+            .filterValuesNotNull()
     }
 
     private fun swipeDownFromTop(pointerCount: Int): Swipe {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 8b5b347..5408428 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.util.MathUtils
+import com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
@@ -24,12 +25,17 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason.FOLD
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.transform
 
 /**
  * Breaks down LOCKSCREEN->AOD transition into discrete steps for corresponding views to consume.
@@ -40,6 +46,7 @@
 @Inject
 constructor(
     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
+    private val powerInteractor: PowerInteractor,
     shadeDependentFlows: ShadeDependentFlows,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
@@ -50,6 +57,12 @@
             edge = Edge.create(from = LOCKSCREEN, to = AOD),
         )
 
+    private val transitionAnimationOnFold =
+        animationFlow.setup(
+            duration = FromLockscreenTransitionInteractor.TO_AOD_FOLD_DURATION,
+            edge = Edge.create(from = LOCKSCREEN, to = AOD),
+        )
+
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(0f),
@@ -71,11 +84,64 @@
 
     fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
         var startAlpha = 1f
-        return transitionAnimation.sharedFlow(
-            duration = 500.milliseconds,
-            onStart = { startAlpha = viewState.alpha() },
-            onStep = { MathUtils.lerp(startAlpha, 1f, it) },
-        )
+        return transitionAnimation
+            .sharedFlow(
+                duration = 500.milliseconds,
+                onStart = { startAlpha = viewState.alpha() },
+                onStep = { MathUtils.lerp(startAlpha, 1f, it) },
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (alpha, wakefulness) ->
+                if (wakefulness.lastSleepReason != FOLD) {
+                    emit(alpha)
+                }
+            }
+    }
+
+    val lockscreenAlphaOnFold: Flow<Float> =
+        transitionAnimationOnFold
+            .sharedFlow(
+                startTime = 600.milliseconds,
+                duration = 500.milliseconds,
+                onStep = { it },
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (alpha, wakefulness) ->
+                if (wakefulness.lastSleepReason == FOLD) {
+                    emit(alpha)
+                }
+            }
+
+    val notificationAlphaOnFold: Flow<Float> =
+        transitionAnimationOnFold
+            .sharedFlow(
+                duration = 1100.milliseconds,
+                onStep = { 0f },
+                onFinish = { 1f },
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (alpha, wakefulness) ->
+                if (wakefulness.lastSleepReason == FOLD) {
+                    emit(alpha)
+                }
+            }
+
+    /** x-translation from the side of the screen for fold animation */
+    fun enterFromSideTranslationX(translatePx: Int): Flow<StateToValue> {
+        return transitionAnimationOnFold
+            .sharedFlowWithState(
+                startTime = 600.milliseconds,
+                duration = 500.milliseconds,
+                onStep = { translatePx + it * -translatePx },
+                onFinish = { 0f },
+                interpolator = EMPHASIZED_DECELERATE,
+            )
+            .sample(powerInteractor.detailedWakefulness, ::Pair)
+            .transform { (stateToValue, wakefulness) ->
+                if (wakefulness.lastSleepReason == FOLD) {
+                    emit(stateToValue)
+                }
+            }
     }
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
index e4465ac..6351d7d 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
@@ -19,10 +19,13 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
+import com.android.systemui.log.echo.LogcatEchoTrackerAlways
 import javax.inject.Inject
 
 @SysUISingleton
-class LogBufferFactory @Inject constructor(
+class LogBufferFactory
+@Inject
+constructor(
     private val dumpManager: DumpManager,
     private val logcatEchoTracker: LogcatEchoTracker
 ) {
@@ -30,9 +33,11 @@
     fun create(
         name: String,
         maxSize: Int,
-        systrace: Boolean = true
+        systrace: Boolean = true,
+        alwaysLogToLogcat: Boolean = false,
     ): LogBuffer {
-        val buffer = LogBuffer(name, adjustMaxSize(maxSize), logcatEchoTracker, systrace)
+        val echoTracker = if (alwaysLogToLogcat) LogcatEchoTrackerAlways else logcatEchoTracker
+        val buffer = LogBuffer(name, adjustMaxSize(maxSize), echoTracker, systrace)
         dumpManager.registerBuffer(name, buffer)
         return buffer
     }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 14890d7..c7fde48 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -364,6 +364,16 @@
         return factory.create("MediaCarouselCtlrLog", 20);
     }
 
+    /**
+     * Provides a buffer for media loading changes
+     */
+    @Provides
+    @SysUISingleton
+    @MediaLoadingLog
+    public static LogBuffer providesMediaLoadingLogBuffer(LogBufferFactory factory) {
+        return factory.create("MediaLoadingLog", 20);
+    }
+
     /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
     @Provides
     @SysUISingleton
@@ -612,7 +622,8 @@
     @SysUISingleton
     @SceneFrameworkLog
     public static LogBuffer provideSceneFrameworkLogBuffer(LogBufferFactory factory) {
-        return factory.create("SceneFramework", 50);
+        return factory
+                .create("SceneFramework", 50, /* systrace */ true, /* alwaysLogToLogcat */  true);
     }
 
     /** Provides a {@link LogBuffer} for the bluetooth QS tile dialog. */
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaLoadingLog.kt
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
copy to packages/SystemUI/src/com/android/systemui/log/dagger/MediaLoadingLog.kt
index 23dbc26..05e1b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/MediaLoadingLog.kt
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recordissue
+package com.android.systemui.log.dagger
 
-import com.android.traceur.TraceUtils.PresetTraceType
+import com.android.systemui.log.LogBuffer
+import javax.inject.Qualifier
 
-data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
+/** A [LogBuffer] for [com.android.systemui.media.controls.domain.pipeline.MediaLoadingLogger] */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class MediaLoadingLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerAlways.kt b/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerAlways.kt
new file mode 100644
index 0000000..ce096b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/echo/LogcatEchoTrackerAlways.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.log.echo
+
+import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
+
+/**
+ * The buffer and all of its tags will be logged to logcat at all times.
+ *
+ * This can be used for buffers that are important and should appear in bugreports in logcat
+ * directly.
+ */
+object LogcatEchoTrackerAlways : LogcatEchoTracker {
+    override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = true
+
+    override fun isTagLoggable(tagName: String, level: LogLevel): Boolean = true
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 1b3b473..988fe64 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -43,7 +43,7 @@
 public class NotificationPlayer implements OnCompletionListener, OnErrorListener {
     private static final int PLAY = 1;
     private static final int STOP = 2;
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private static final class Command {
         int code;
diff --git a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
index 3ab0420..e7c2a45 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/RingtonePlayer.java
@@ -53,7 +53,7 @@
 @SysUISingleton
 public class RingtonePlayer implements CoreStartable {
     private static final String TAG = "RingtonePlayer";
-    private static final boolean LOGD = false;
+    private static final boolean LOGD = true;
     private final Context mContext;
 
     // TODO: support Uri switching under same IBinder
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
index 0c70f10..6a91d1b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt
@@ -218,9 +218,9 @@
                         mediaFromRecPackageName = null
                         _currentMedia.value = sortedMap.values.toList()
                     }
-                } else if (sortedMap.size > sortedMedia.size) {
+                } else if (sortedMap.size > _currentMedia.value.size && it.active) {
                     _currentMedia.value = sortedMap.values.toList()
-                } else if (sortedMap.size == sortedMedia.size) {
+                } else {
                     // When loading an update for an existing media control.
                     val currentList =
                         mutableListOf<MediaCommonModel>().apply { addAll(_currentMedia.value) }
@@ -296,6 +296,18 @@
         mediaFromRecPackageName = packageName
     }
 
+    fun hasActiveMedia(): Boolean {
+        return _selectedUserEntries.value.any { it.value.active }
+    }
+
+    fun hasAnyMedia(): Boolean {
+        return _selectedUserEntries.value.entries.isNotEmpty()
+    }
+
+    fun isRecommendationActive(): Boolean {
+        return _smartspaceMediaData.value.isActive
+    }
+
     private fun canBeRemoved(data: MediaData): Boolean {
         return data.isPlaying?.let { !it } ?: data.isClearable && !data.active
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index 8d19ce8..f78a0f9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -24,6 +24,7 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.logging.InstanceId
 import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.controls.data.repository.MediaFilterRepository
 import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_RESUME
@@ -56,6 +57,7 @@
  * This is added at the end of the pipeline since we may still need to handle callbacks from
  * background users (e.g. timeouts).
  */
+@SysUISingleton
 class MediaDataFilterImpl
 @Inject
 constructor(
@@ -68,11 +70,13 @@
     private val logger: MediaUiEventLogger,
     private val mediaFlags: MediaFlags,
     private val mediaFilterRepository: MediaFilterRepository,
+    private val mediaLoadingLogger: MediaLoadingLogger,
 ) : MediaDataManager.Listener {
     /** Non-UI listeners to media changes. */
     private val _listeners: MutableSet<MediaDataProcessor.Listener> = mutableSetOf()
     val listeners: Set<MediaDataProcessor.Listener>
         get() = _listeners.toSet()
+
     lateinit var mediaDataProcessor: MediaDataProcessor
 
     // Ensure the field (and associated reference) isn't removed during optimization.
@@ -114,6 +118,7 @@
 
         mediaFilterRepository.addSelectedUserMediaEntry(data)
 
+        mediaLoadingLogger.logMediaLoaded(data.instanceId, data.active, "loading media")
         mediaFilterRepository.addMediaDataLoadingState(
             MediaDataLoadingModel.Loaded(data.instanceId)
         )
@@ -167,7 +172,6 @@
             if (shouldReactivate) {
                 val lastActiveId = sorted.lastKey() // most recently active id
                 // Update loading state to consider this media active
-                Log.d(TAG, "reactivating $lastActiveId instead of smartspace")
                 mediaFilterRepository.setReactivatedId(lastActiveId)
                 val mediaData = sorted[lastActiveId]!!.copy(active = true)
                 logger.logRecommendationActivated(
@@ -178,6 +182,11 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Loaded(lastActiveId)
                 )
+                mediaLoadingLogger.logMediaLoaded(
+                    mediaData.instanceId,
+                    mediaData.active,
+                    "reactivating media instead of smartspace"
+                )
                 listeners.forEach { listener ->
                     getKey(lastActiveId)?.let { lastActiveKey ->
                         listener.onMediaDataLoaded(
@@ -210,6 +219,7 @@
         mediaFilterRepository.setRecommendationsLoadingState(
             SmartspaceMediaLoadingModel.Loaded(key, shouldPrioritizeMutable)
         )
+        mediaLoadingLogger.logRecommendationLoaded(key, data.isActive, "loading recommendations")
         listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
     }
 
@@ -220,6 +230,7 @@
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Removed(instanceId)
                 )
+                mediaLoadingLogger.logMediaRemoved(instanceId, "removing media card")
                 // Only notify listeners if something actually changed
                 listeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
             }
@@ -230,12 +241,16 @@
         // First check if we had reactivated media instead of forwarding smartspace
         mediaFilterRepository.reactivatedId.value?.let { lastActiveId ->
             mediaFilterRepository.setReactivatedId(null)
-            Log.d(TAG, "expiring reactivated key $lastActiveId")
             // Update loading state with actual active value
             mediaFilterRepository.selectedUserEntries.value[lastActiveId]?.let {
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Loaded(lastActiveId, immediately)
                 )
+                mediaLoadingLogger.logMediaLoaded(
+                    lastActiveId,
+                    it.active,
+                    "expiring reactivated id"
+                )
                 listeners.forEach { listener ->
                     getKey(lastActiveId)?.let { lastActiveKey ->
                         listener.onMediaDataLoaded(lastActiveKey, lastActiveKey, it, immediately)
@@ -256,6 +271,11 @@
         mediaFilterRepository.setRecommendationsLoadingState(
             SmartspaceMediaLoadingModel.Removed(key, immediately)
         )
+        mediaLoadingLogger.logRecommendationRemoved(
+            key,
+            immediately,
+            "removing recommendations card"
+        )
         listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) }
     }
 
@@ -265,11 +285,14 @@
         mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
             if (!lockscreenUserManager.isProfileAvailable(data.userId)) {
                 // Only remove media when the profile is unavailable.
-                if (DEBUG) Log.d(TAG, "Removing $key after profile change")
                 mediaFilterRepository.removeSelectedUserMediaEntry(data.instanceId, data)
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Removed(data.instanceId)
                 )
+                mediaLoadingLogger.logMediaRemoved(
+                    data.instanceId,
+                    "Removing $key after profile change"
+                )
                 listeners.forEach { listener -> listener.onMediaDataRemoved(key, false) }
             }
         }
@@ -283,10 +306,10 @@
         // Clear the list first and update loading state to remove media from UI.
         mediaFilterRepository.clearSelectedUserMedia()
         keyCopy.forEach { instanceId ->
-            if (DEBUG) Log.d(TAG, "Removing $instanceId after user change")
             mediaFilterRepository.addMediaDataLoadingState(
                 MediaDataLoadingModel.Removed(instanceId)
             )
+            mediaLoadingLogger.logMediaRemoved(instanceId, "Removing media after user change")
             getKey(instanceId)?.let {
                 listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it, false) }
             }
@@ -294,15 +317,15 @@
 
         mediaFilterRepository.allUserEntries.value.forEach { (key, data) ->
             if (lockscreenUserManager.isCurrentProfile(data.userId)) {
-                if (DEBUG)
-                    Log.d(
-                        TAG,
-                        "Re-adding $key with instanceId=${data.instanceId} after user change"
-                    )
                 mediaFilterRepository.addSelectedUserMediaEntry(data)
                 mediaFilterRepository.addMediaDataLoadingState(
                     MediaDataLoadingModel.Loaded(data.instanceId)
                 )
+                mediaLoadingLogger.logMediaLoaded(
+                    data.instanceId,
+                    data.active,
+                    "Re-adding $key after user change"
+                )
                 listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 8e985e1..37dffd1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -107,6 +107,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
@@ -371,6 +372,7 @@
             .onStart { emit(Unit) }
             .map { allowMediaRecommendations() }
             .distinctUntilChanged()
+            .flowOn(backgroundDispatcher)
             // only track the most recent emission
             .collectLatest {
                 allowMediaRecommendations = it
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index aa93df7..8b2f619 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -358,7 +358,12 @@
                             // LocalMediaManager. Override with routing session name if available to
                             // show dynamic group name.
                             connectedDevice?.copy(name = it.name ?: connectedDevice.name)
-                        }
+                        } ?: MediaDeviceData(
+                            enabled = false,
+                            icon = context.getDrawable(R.drawable.ic_media_home_devices),
+                            name = context.getString(R.string.media_seamless_other_device),
+                            showBroadcastButton = false
+                        )
                 } else {
                     // Prefer SASS if available when playback is local.
                     activeDevice = getSassDevice() ?: connectedDevice
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaLoadingLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaLoadingLogger.kt
new file mode 100644
index 0000000..c6cfd65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaLoadingLogger.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.domain.pipeline
+
+import com.android.internal.logging.InstanceId
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.MediaLoadingLog
+import javax.inject.Inject
+
+/** A buffered log for media loading events. */
+@SysUISingleton
+class MediaLoadingLogger @Inject constructor(@MediaLoadingLog private val buffer: LogBuffer) {
+
+    fun logMediaLoaded(instanceId: InstanceId, active: Boolean, reason: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = instanceId.toString()
+                bool1 = active
+                str2 = reason
+            },
+            { "add media $str1, active: $bool1, reason: $str2" }
+        )
+    }
+
+    fun logMediaRemoved(instanceId: InstanceId, reason: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = instanceId.toString()
+                str2 = reason
+            },
+            { "removing media $str1, reason: $str2" }
+        )
+    }
+
+    fun logRecommendationLoaded(key: String, isActive: Boolean, reason: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = key
+                bool1 = isActive
+                str2 = reason
+            },
+            { "add recommendation $str1, active $bool1, reason: $str2" }
+        )
+    }
+
+    fun logRecommendationRemoved(key: String, immediately: Boolean, reason: String) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = key
+                bool1 = immediately
+                str2 = reason
+            },
+            { "removing recommendation $str1, immediate=$bool1, reason: $str2" }
+        )
+    }
+
+    companion object {
+        private const val TAG = "MediaLoadingLog"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index b4bd4fd..0630cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.media.controls.data.repository.MediaDataRepository
 import com.android.systemui.media.controls.data.repository.MediaFilterRepository
 import com.android.systemui.media.controls.domain.pipeline.MediaDataCombineLatest
 import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
@@ -45,7 +44,6 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates business logic for media pipeline. */
@@ -55,7 +53,6 @@
 @Inject
 constructor(
     @Application applicationScope: CoroutineScope,
-    private val mediaDataRepository: MediaDataRepository,
     private val mediaDataProcessor: MediaDataProcessor,
     private val mediaTimeoutListener: MediaTimeoutListener,
     private val mediaResumeListener: MediaResumeListener,
@@ -103,26 +100,6 @@
                 initialValue = false,
             )
 
-    /** Are there any media notifications active, excluding the recommendations? */
-    val hasActiveMedia: StateFlow<Boolean> =
-        mediaFilterRepository.selectedUserEntries
-            .mapLatest { entries -> entries.any { it.value.active } }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = false,
-            )
-
-    /** Are there any media notifications, excluding the recommendations? */
-    val hasAnyMedia: StateFlow<Boolean> =
-        mediaFilterRepository.selectedUserEntries
-            .mapLatest { entries -> entries.isNotEmpty() }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = false,
-            )
-
     /** The current list for user media instances */
     val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia
 
@@ -235,11 +212,11 @@
 
     override fun hasAnyMediaOrRecommendation() = hasAnyMediaOrRecommendation.value
 
-    override fun hasActiveMedia() = hasActiveMedia.value
+    override fun hasActiveMedia() = mediaFilterRepository.hasActiveMedia()
 
-    override fun hasAnyMedia() = hasAnyMedia.value
+    override fun hasAnyMedia() = mediaFilterRepository.hasAnyMedia()
 
-    override fun isRecommendationActive() = mediaDataRepository.smartspaceMediaData.value.isActive
+    override fun isRecommendationActive() = mediaFilterRepository.isRecommendationActive()
 
     fun reorderMedia() {
         mediaFilterRepository.setOrderedMedia()
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 a70bf91..53794d2 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
@@ -215,7 +215,7 @@
     private var carouselLocale: Locale? = null
 
     private val animationScaleObserver: ContentObserver =
-        object : ContentObserver(null) {
+        object : ContentObserver(executor, 0) {
             override fun onChange(selfChange: Boolean) {
                 if (!mediaFlags.isSceneContainerEnabled()) {
                     MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() }
@@ -395,10 +395,12 @@
         }
 
         // Notifies all active players about animation scale changes.
-        globalSettings.registerContentObserverSync(
-            Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
-            animationScaleObserver
-        )
+        bgExecutor.execute {
+            globalSettings.registerContentObserverSync(
+                    Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+                    animationScaleObserver
+            )
+        }
     }
 
     private fun setUpListeners() {
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 601d563..88a28bf 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
@@ -36,6 +36,7 @@
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.traceSection
 import com.android.keyguard.KeyguardViewController
+import com.android.systemui.Flags.mediaControlsLockscreenShadeBugFix
 import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -483,8 +484,7 @@
             object : StatusBarStateController.StateListener {
                 override fun onStatePreChange(oldState: Int, newState: Int) {
                     // We're updating the location before the state change happens, since we want
-                    // the
-                    // location of the previous state to still be up to date when the animation
+                    // the location of the previous state to still be up to date when the animation
                     // starts
                     if (
                         newState == StatusBarState.SHADE_LOCKED &&
@@ -588,6 +588,17 @@
             }
         }
 
+        if (mediaControlsLockscreenShadeBugFix()) {
+            coroutineScope.launch {
+                shadeInteractor.shadeExpansion.collect { expansion ->
+                    if (expansion >= 1f || expansion <= 0f) {
+                        // Shade has fully expanded or collapsed: force transition amount update
+                        setTransitionToFullShadeAmount(expansion)
+                    }
+                }
+            }
+        }
+
         val settingsObserver: ContentObserver =
             object : ContentObserver(handler) {
                 override fun onChange(selfChange: Boolean, uri: Uri?) {
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 4e90936..f0d8df5 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
@@ -155,13 +155,17 @@
                             mediaFlags.isPersistentSsCardEnabled(),
                     recsViewModel = recommendationsViewModel,
                     onAdded = { commonViewModel ->
-                        onMediaRecommendationAddedOrUpdated(commonViewModel)
+                        onMediaRecommendationAddedOrUpdated(
+                            commonViewModel as MediaCommonViewModel.MediaRecommendations
+                        )
                     },
                     onRemoved = { immediatelyRemove ->
                         onMediaRecommendationRemoved(commonModel, immediatelyRemove)
                     },
                     onUpdated = { commonViewModel ->
-                        onMediaRecommendationAddedOrUpdated(commonViewModel)
+                        onMediaRecommendationAddedOrUpdated(
+                            commonViewModel as MediaCommonViewModel.MediaRecommendations
+                        )
                     },
                 )
                 .also { mediaRecs = it }
@@ -185,7 +189,9 @@
         }
     }
 
-    private fun onMediaRecommendationAddedOrUpdated(commonViewModel: MediaCommonViewModel) {
+    private fun onMediaRecommendationAddedOrUpdated(
+        commonViewModel: MediaCommonViewModel.MediaRecommendations
+    ) {
         if (!interactor.isRecommendationActive()) {
             if (!mediaFlags.isPersistentSsCardEnabled()) {
                 commonViewModel.onRemoved(true)
@@ -201,6 +207,7 @@
     ) {
         if (immediatelyRemove || isReorderingAllowed()) {
             interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L)
+            mediaRecs = null
             if (!immediatelyRemove) {
                 // Although it wasn't requested, we were able to process the removal
                 // immediately since reordering is allowed. So, notify hosts to update
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
index fbb84de..4496b25 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.media.dialog;
 
-import androidx.annotation.IntDef;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.res.R;
@@ -46,40 +48,50 @@
         int TYPE_PAIR_NEW_DEVICE = 2;
     }
 
-    public MediaItem() {
-        this.mMediaDeviceOptional = Optional.empty();
-        this.mTitle = null;
-        this.mMediaItemType = MediaItemType.TYPE_PAIR_NEW_DEVICE;
+    /**
+     * Returns a new {@link MediaItemType#TYPE_DEVICE} {@link MediaItem} with its {@link
+     * #getMediaDevice() media device} set to {@code device} and its title set to {@code device}'s
+     * name.
+     */
+    public static MediaItem createDeviceMediaItem(@NonNull MediaDevice device) {
+        return new MediaItem(device, device.getName(), MediaItemType.TYPE_DEVICE);
     }
 
-    public MediaItem(String title, int mediaItemType) {
-        this.mMediaDeviceOptional = Optional.empty();
+    /**
+     * Returns a new {@link MediaItemType#TYPE_PAIR_NEW_DEVICE} {@link MediaItem} with both {@link
+     * #getMediaDevice() media device} and title set to {@code null}.
+     */
+    public static MediaItem createPairNewDeviceMediaItem() {
+        return new MediaItem(
+                /* device */ null, /* title */ null, MediaItemType.TYPE_PAIR_NEW_DEVICE);
+    }
+
+    /**
+     * Returns a new {@link MediaItemType#TYPE_GROUP_DIVIDER} {@link MediaItem} with the specified
+     * title and a {@code null} {@link #getMediaDevice() media device}.
+     */
+    public static MediaItem createGroupDividerMediaItem(@Nullable String title) {
+        return new MediaItem(/* device */ null, title, MediaItemType.TYPE_GROUP_DIVIDER);
+    }
+
+    private MediaItem(
+            @Nullable MediaDevice device, @Nullable String title, @MediaItemType int type) {
+        this.mMediaDeviceOptional = Optional.ofNullable(device);
         this.mTitle = title;
-        this.mMediaItemType = mediaItemType;
-    }
-
-    public MediaItem(MediaDevice mediaDevice) {
-        this.mMediaDeviceOptional = Optional.of(mediaDevice);
-        this.mTitle = mediaDevice.getName();
-        this.mMediaItemType = MediaItemType.TYPE_DEVICE;
+        this.mMediaItemType = type;
     }
 
     public Optional<MediaDevice> getMediaDevice() {
         return mMediaDeviceOptional;
     }
 
-    /**
-     * Get layout id based on media item Type.
-     */
-    public static int getMediaLayoutId(int mediaItemType) {
-        switch (mediaItemType) {
-            case MediaItemType.TYPE_DEVICE:
-            case MediaItemType.TYPE_PAIR_NEW_DEVICE:
-                return R.layout.media_output_list_item_advanced;
-            case MediaItemType.TYPE_GROUP_DIVIDER:
-            default:
-                return R.layout.media_output_list_group_divider;
-        }
+    /** Get layout id based on media item Type. */
+    public static int getMediaLayoutId(@MediaItemType int mediaItemType) {
+        return switch (mediaItemType) {
+            case MediaItemType.TYPE_DEVICE, MediaItemType.TYPE_PAIR_NEW_DEVICE ->
+                    R.layout.media_output_list_item_advanced;
+            default -> R.layout.media_output_list_group_divider;
+        };
     }
 
     public String getTitle() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index c2cfdbe..1e86563 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -68,6 +68,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.graphics.drawable.IconCompat;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -656,10 +657,16 @@
                     if (DEBUG) {
                         Log.d(TAG, "No connected media device or muting expected device exist.");
                     }
-                    return categorizeMediaItems(null, devices, needToHandleMutingExpectedDevice);
+                    return categorizeMediaItemsLocked(
+                            /* connectedMediaDevice */ null,
+                            devices,
+                            needToHandleMutingExpectedDevice);
                 }
                 // selected device exist
-                return categorizeMediaItems(connectedMediaDevice, devices, false);
+                return categorizeMediaItemsLocked(
+                        connectedMediaDevice,
+                        devices,
+                        /* needToHandleMutingExpectedDevice */ false);
             }
             // To keep the same list order
             final List<MediaDevice> targetMediaDevices = new ArrayList<>();
@@ -682,8 +689,9 @@
                 devices.removeAll(targetMediaDevices);
                 targetMediaDevices.addAll(devices);
             }
-            List<MediaItem> finalMediaItems = targetMediaDevices.stream().map(
-                    MediaItem::new).collect(Collectors.toList());
+            List<MediaItem> finalMediaItems = targetMediaDevices.stream()
+                    .map(MediaItem::createDeviceMediaItem)
+                    .collect(Collectors.toList());
             dividerItems.forEach(finalMediaItems::add);
             attachConnectNewDeviceItemIfNeeded(finalMediaItems);
             return finalMediaItems;
@@ -694,51 +702,50 @@
      * Initial categorization of current devices, will not be called for updates to the devices
      * list.
      */
-    private List<MediaItem> categorizeMediaItems(MediaDevice connectedMediaDevice,
+    @GuardedBy("mMediaDevicesLock")
+    private List<MediaItem> categorizeMediaItemsLocked(MediaDevice connectedMediaDevice,
             List<MediaDevice> devices,
             boolean needToHandleMutingExpectedDevice) {
-        synchronized (mMediaDevicesLock) {
-            List<MediaItem> finalMediaItems = new ArrayList<>();
-            Set<String> selectedDevicesIds = getSelectedMediaDevice().stream().map(
-                    MediaDevice::getId).collect(Collectors.toSet());
-            if (connectedMediaDevice != null) {
-                selectedDevicesIds.add(connectedMediaDevice.getId());
-            }
-            boolean suggestedDeviceAdded = false;
-            boolean displayGroupAdded = false;
-            for (MediaDevice device : devices) {
-                if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) {
-                    finalMediaItems.add(0, new MediaItem(device));
-                } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains(
-                        device.getId())) {
-                    finalMediaItems.add(0, new MediaItem(device));
-                } else {
-                    if (device.isSuggestedDevice() && !suggestedDeviceAdded) {
-                        attachGroupDivider(finalMediaItems, mContext.getString(
-                                R.string.media_output_group_title_suggested_device));
-                        suggestedDeviceAdded = true;
-                    } else if (!device.isSuggestedDevice() && !displayGroupAdded) {
-                        attachGroupDivider(finalMediaItems, mContext.getString(
-                                R.string.media_output_group_title_speakers_and_displays));
-                        displayGroupAdded = true;
-                    }
-                    finalMediaItems.add(new MediaItem(device));
-                }
-            }
-            attachConnectNewDeviceItemIfNeeded(finalMediaItems);
-            return finalMediaItems;
+        List<MediaItem> finalMediaItems = new ArrayList<>();
+        Set<String> selectedDevicesIds = getSelectedMediaDevice().stream()
+                .map(MediaDevice::getId)
+                .collect(Collectors.toSet());
+        if (connectedMediaDevice != null) {
+            selectedDevicesIds.add(connectedMediaDevice.getId());
         }
+        boolean suggestedDeviceAdded = false;
+        boolean displayGroupAdded = false;
+        for (MediaDevice device : devices) {
+            if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) {
+                finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device));
+            } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains(
+                    device.getId())) {
+                finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device));
+            } else {
+                if (device.isSuggestedDevice() && !suggestedDeviceAdded) {
+                    attachGroupDivider(finalMediaItems, mContext.getString(
+                            R.string.media_output_group_title_suggested_device));
+                    suggestedDeviceAdded = true;
+                } else if (!device.isSuggestedDevice() && !displayGroupAdded) {
+                    attachGroupDivider(finalMediaItems, mContext.getString(
+                            R.string.media_output_group_title_speakers_and_displays));
+                    displayGroupAdded = true;
+                }
+                finalMediaItems.add(MediaItem.createDeviceMediaItem(device));
+            }
+        }
+        attachConnectNewDeviceItemIfNeeded(finalMediaItems);
+        return finalMediaItems;
     }
 
     private void attachGroupDivider(List<MediaItem> mediaItems, String title) {
-        mediaItems.add(
-                new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER));
+        mediaItems.add(MediaItem.createGroupDividerMediaItem(title));
     }
 
     private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) {
         // Attach "Connect a device" item only when current output is not remote and not a group
         if (!isCurrentConnectedDeviceRemote() && getSelectedMediaDevice().size() == 1) {
-            mediaItems.add(new MediaItem());
+            mediaItems.add(MediaItem.createPairNewDeviceMediaItem());
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt
index f47954a..3a292e7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt
@@ -94,7 +94,7 @@
     }
 
     private fun AudioDeviceAttributes.getIcon(): Drawable {
-        return deviceIconUtil.getIconFromAudioDeviceType(this.type, context)
+        return deviceIconUtil.getIconFromAudioDeviceType(this.type)
     }
 
     private fun IntArray.hasMedia() = USAGE_MEDIA in this
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 596c18f..01b1be9 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -53,8 +53,13 @@
         withContext(coroutineDispatcher) {
             val groupedTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
             // Note: the returned task list is from the most-recent to least-recent order.
-            // The last foreground task is at index 1, because at index 0 will be our app selector.
-            val foregroundGroup = groupedTasks.elementAtOrNull(1)
+            // When opening the app selector in full screen, index 0 will be just the app selector
+            // activity and a null second task, so the foreground task will be index 1, but when
+            // opening the app selector in split screen mode, the foreground task will be the second
+            // task in index 0.
+            val foregroundGroup =
+                if (groupedTasks.firstOrNull()?.splitBounds != null) groupedTasks.first()
+                else groupedTasks.elementAtOrNull(1)
             val foregroundTaskId1 = foregroundGroup?.taskInfo1?.taskId
             val foregroundTaskId2 = foregroundGroup?.taskInfo2?.taskId
             val foregroundTaskIds = listOfNotNull(foregroundTaskId1, foregroundTaskId2)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
index 1d5f6f5..de300b2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
@@ -20,7 +20,21 @@
 
 /** Represents the state of media projection. */
 sealed interface MediaProjectionState {
-    object NotProjecting : MediaProjectionState
-    object EntireScreen : MediaProjectionState
-    data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState
+    /** There is no media being projected. */
+    data object NotProjecting : MediaProjectionState
+
+    /**
+     * Media is currently being projected.
+     *
+     * @property hostPackage the package name of the app that is receiving the content of the media
+     *   projection (aka which app the phone screen contents are being sent to).
+     */
+    sealed class Projecting(open val hostPackage: String) : MediaProjectionState {
+        /** The entire screen is being projected. */
+        data class EntireScreen(override val hostPackage: String) : Projecting(hostPackage)
+
+        /** Only a single task is being projected. */
+        data class SingleTask(override val hostPackage: String, val task: RunningTaskInfo) :
+            Projecting(hostPackage)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
index 3ce0a1e0..8a9adc7 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
@@ -64,6 +64,10 @@
         }
     }
 
+    override suspend fun stopProjecting() {
+        withContext(backgroundDispatcher) { mediaProjectionManager.stopActiveProjection() }
+    }
+
     override val mediaProjectionState: Flow<MediaProjectionState> =
         conflatedCallbackFlow {
                 val callback =
@@ -83,7 +87,9 @@
                             session: ContentRecordingSession?
                         ) {
                             Log.d(TAG, "MediaProjectionManager.Callback#onSessionStarted: $session")
-                            launch { trySendWithFailureLogging(stateForSession(session), TAG) }
+                            launch {
+                                trySendWithFailureLogging(stateForSession(info, session), TAG)
+                            }
                         }
                     }
                 mediaProjectionManager.addCallback(callback, handler)
@@ -95,19 +101,23 @@
                 initialValue = MediaProjectionState.NotProjecting,
             )
 
-    private suspend fun stateForSession(session: ContentRecordingSession?): MediaProjectionState {
+    private suspend fun stateForSession(
+        info: MediaProjectionInfo,
+        session: ContentRecordingSession?
+    ): MediaProjectionState {
         if (session == null) {
             return MediaProjectionState.NotProjecting
         }
+
+        val hostPackage = info.packageName
         if (session.contentToRecord == RECORD_CONTENT_DISPLAY || session.tokenToRecord == null) {
-            return MediaProjectionState.EntireScreen
+            return MediaProjectionState.Projecting.EntireScreen(hostPackage)
         }
         val matchingTask =
             tasksRepository.findRunningTaskFromWindowContainerToken(
                 checkNotNull(session.tokenToRecord)
-            )
-                ?: return MediaProjectionState.EntireScreen
-        return MediaProjectionState.SingleTask(matchingTask)
+            ) ?: return MediaProjectionState.Projecting.EntireScreen(hostPackage)
+        return MediaProjectionState.Projecting.SingleTask(hostPackage, matchingTask)
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
index 21300db..50182d7 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
@@ -26,6 +26,9 @@
     /** Switches the task that should be projected. */
     suspend fun switchProjectedTask(task: RunningTaskInfo)
 
+    /** Stops the currently active projection. */
+    suspend fun stopProjecting()
+
     /** Represents the current [MediaProjectionState]. */
     val mediaProjectionState: Flow<MediaProjectionState>
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index e861ddf..f004c3a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -23,6 +23,7 @@
 import static android.media.projection.MediaProjectionManager.OVERRIDE_DISABLE_MEDIA_PROJECTION_SINGLE_APP_OPTION;
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL;
 import static android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_DISPLAY;
+import static android.os.UserHandle.USER_SYSTEM;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import static com.android.systemui.mediaprojection.permission.ScreenShareOptionKt.ENTIRE_SCREEN;
@@ -31,7 +32,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.ActivityOptions.LaunchCookie;
 import android.app.AlertDialog;
 import android.app.StatusBarManager;
@@ -366,11 +366,11 @@
                 intent.putExtra(EXTRA_USER_REVIEW_GRANTED_CONSENT, mReviewGrantedConsentRequired);
                 intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
 
-                // Start activity from the current foreground user to avoid creating a separate
-                // SystemUI process without access to recent tasks because it won't have
-                // WM Shell running inside.
+                // Start activity as system user and manually show app selector to all users to
+                // avoid creating a separate SystemUI process without access to recent tasks
+                // because it won't have WM Shell running inside.
                 mUserSelectingTask = true;
-                startActivityAsUser(intent, UserHandle.of(ActivityManager.getCurrentUser()));
+                startActivityAsUser(intent, UserHandle.of(USER_SYSTEM));
                 // close shade if it's open
                 mStatusBarManager.collapsePanels();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
index c232d4d..118639c 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
@@ -57,7 +57,7 @@
         mediaProjectionRepository.mediaProjectionState.flatMapLatest { projectionState ->
             Log.d(TAG, "MediaProjectionState -> $projectionState")
             when (projectionState) {
-                is MediaProjectionState.SingleTask -> {
+                is MediaProjectionState.Projecting.SingleTask -> {
                     val projectedTask = projectionState.task
                     tasksRepository.foregroundTask.map { foregroundTask ->
                         if (hasForegroundTaskSwitched(projectedTask, foregroundTask)) {
@@ -67,7 +67,7 @@
                         }
                     }
                 }
-                is MediaProjectionState.EntireScreen,
+                is MediaProjectionState.Projecting.EntireScreen,
                 is MediaProjectionState.NotProjecting -> {
                     flowOf(TaskSwitchState.NotProjectingTask)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigation/data/repository/NavigationRepository.kt b/packages/SystemUI/src/com/android/systemui/navigation/data/repository/NavigationRepository.kt
new file mode 100644
index 0000000..4409e2c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigation/data/repository/NavigationRepository.kt
@@ -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.systemui.navigation.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class NavigationRepository
+@Inject
+constructor(
+    private val controller: NavigationModeController,
+) {
+
+    /** Whether the current navigation bar mode is edge-to-edge. */
+    val isGesturalMode: Flow<Boolean> = conflatedCallbackFlow {
+        val listener =
+            NavigationModeController.ModeChangedListener { mode ->
+                trySend(QuickStepContract.isGesturalMode(mode))
+            }
+
+        val currentMode = controller.addListener(listener)
+        trySend(QuickStepContract.isGesturalMode(currentMode))
+
+        awaitClose { controller.removeListener(listener) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/src/com/android/systemui/navigation/domain/interactor/NavigationInteractor.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
copy to packages/SystemUI/src/com/android/systemui/navigation/domain/interactor/NavigationInteractor.kt
index 2e9169e..0f9c883 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigation/domain/interactor/NavigationInteractor.kt
@@ -13,17 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.bluetooth.qsdialog
+
+package com.android.systemui.navigation.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
+import com.android.systemui.navigation.data.repository.NavigationRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
 
-@Module
-interface BluetoothTileDialogModule {
-    @Binds
-    @SysUISingleton
-    fun bindDeviceItemActionInteractor(
-        impl: DeviceItemActionInteractorImpl
-    ): DeviceItemActionInteractor
+@SysUISingleton
+class NavigationInteractor
+@Inject
+constructor(
+    repository: NavigationRepository,
+) {
+
+    /** Whether the current navigation bar mode is edge-to-edge. */
+    val isGesturalMode: Flow<Boolean> = repository.isGesturalMode
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 0e819c2..07289cb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -2028,12 +2028,15 @@
                     getBarTransitions().setBackgroundOverrideAlpha(1f);
                 }
             }
-            updateScreenPinningGestures();
+
+            // Update the window layout params when the nav mode changes as that will affect the
+            // system gesture insets
+            setNavBarMode(mode);
+            repositionNavigationBar(mCurrentRotation);
 
             if (!canShowSecondaryHandle()) {
                 resetSecondaryHandle();
             }
-            setNavBarMode(mode);
             mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI());
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
index 3268306..8177fde 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/DeadZone.java
@@ -61,6 +61,7 @@
         }
     };
 
+    private final boolean mUseDeadZone;
     private final NavigationBarController mNavBarController;
     private final NavigationBarView mNavigationBarView;
 
@@ -86,9 +87,12 @@
 
     @Inject
     public DeadZone(NavigationBarView view) {
+        mUseDeadZone = view.getResources().getBoolean(R.bool.config_useDeadZone);
+
         mNavigationBarView = view;
         mNavBarController = Dependency.get(NavigationBarController.class);
         mDisplayId = view.getContext().getDisplayId();
+
         onConfigurationChanged(HORIZONTAL);
     }
 
@@ -108,12 +112,20 @@
     }
 
     public void setFlashOnTouchCapture(boolean dbg) {
+        if (!mUseDeadZone) {
+            return;
+        }
+
         mShouldFlash = dbg;
         mFlashFrac = 0f;
         mNavigationBarView.postInvalidate();
     }
 
     public void onConfigurationChanged(int rotation) {
+        if (!mUseDeadZone) {
+            return;
+        }
+
         mDisplayRotation = rotation;
 
         final Resources res = mNavigationBarView.getResources();
@@ -134,6 +146,10 @@
 
     // I made you a touch event...
     public boolean onTouchEvent(MotionEvent event) {
+        if (!mUseDeadZone) {
+            return false;
+        }
+
         if (DEBUG) {
             Slog.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction()));
         }
@@ -187,17 +203,17 @@
         if (mShouldFlash) mNavigationBarView.postInvalidate();
     }
 
-    public void setFlash(float f) {
+    private void setFlash(float f) {
         mFlashFrac = f;
         mNavigationBarView.postInvalidate();
     }
 
-    public float getFlash() {
+    private float getFlash() {
         return mFlashFrac;
     }
 
     public void onDraw(Canvas can) {
-        if (!mShouldFlash || mFlashFrac <= 0f) {
+        if (!mUseDeadZone || !mShouldFlash || mFlashFrac <= 0f) {
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 41cd2c4..c9be993 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -24,6 +24,7 @@
 import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
 import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll;
 import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED;
 
 import static java.util.stream.Collectors.joining;
 
@@ -44,6 +45,7 @@
 import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.icu.text.SimpleDateFormat;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -409,6 +411,7 @@
             PluginManager pluginManager,
             @BackPanelUiThread UiThreadContext uiThreadContext,
             @Background Executor backgroundExecutor,
+            @Background Handler bgHandler,
             UserTracker userTracker,
             NavigationModeController navigationModeController,
             BackPanelController.Factory backPanelControllerFactory,
@@ -472,7 +475,8 @@
                 ViewConfiguration.getLongPressTimeout());
 
         mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
-                mUiThreadContext.getHandler(), mContext, this::onNavigationSettingsChanged);
+                mUiThreadContext.getHandler(), bgHandler, mContext,
+                this::onNavigationSettingsChanged);
 
         updateCurrentUserResources();
     }
@@ -1021,8 +1025,10 @@
             if (mIsTrackpadThreeFingerSwipe) {
                 // Trackpad back gestures don't have zones, so we don't need to check if the down
                 // event is within insets.
-                mAllowGesture = isBackAllowedCommon && isValidTrackpadBackGesture(
-                        true /* isTrackpadEvent */);
+                boolean trackpadGesturesEnabled =
+                        (mSysUiFlags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0;
+                mAllowGesture = isBackAllowedCommon && trackpadGesturesEnabled
+                        && isValidTrackpadBackGesture(true /* isTrackpadEvent */);
             } else {
                 mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets
                     && isWithinTouchRegion((int) ev.getX(), (int) ev.getY())
@@ -1313,6 +1319,7 @@
         private final PluginManager mPluginManager;
         private final UiThreadContext mUiThreadContext;
         private final Executor mBackgroundExecutor;
+        private final Handler mBgHandler;
         private final UserTracker mUserTracker;
         private final NavigationModeController mNavigationModeController;
         private final BackPanelController.Factory mBackPanelControllerFactory;
@@ -1333,6 +1340,7 @@
                         PluginManager pluginManager,
                         @BackPanelUiThread UiThreadContext uiThreadContext,
                         @Background Executor backgroundExecutor,
+                        @Background Handler bgHandler,
                         UserTracker userTracker,
                         NavigationModeController navigationModeController,
                         BackPanelController.Factory backPanelControllerFactory,
@@ -1351,6 +1359,7 @@
             mPluginManager = pluginManager;
             mUiThreadContext = uiThreadContext;
             mBackgroundExecutor = backgroundExecutor;
+            mBgHandler = bgHandler;
             mUserTracker = userTracker;
             mNavigationModeController = navigationModeController;
             mBackPanelControllerFactory = backPanelControllerFactory;
@@ -1375,6 +1384,7 @@
                             mPluginManager,
                             mUiThreadContext,
                             mBackgroundExecutor,
+                            mBgHandler,
                             mUserTracker,
                             mNavigationModeController,
                             mBackPanelControllerFactory,
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
index f677ec1b..d0c7fbc 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModel.kt
@@ -17,41 +17,25 @@
 package com.android.systemui.notifications.ui.viewmodel
 
 import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
+import com.android.systemui.scene.shared.model.SceneFamilies
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.asStateFlow
 
 /** Models UI state and handles user input for the Notifications Shade scene. */
 @SysUISingleton
-class NotificationsShadeSceneViewModel
-@Inject
-constructor(
-    @Application private val applicationScope: CoroutineScope,
-    overlayShadeViewModel: OverlayShadeViewModel,
-) {
+class NotificationsShadeSceneViewModel @Inject constructor() {
     val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
-        overlayShadeViewModel.backgroundScene
-            .map(::destinationScenes)
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = destinationScenes(overlayShadeViewModel.backgroundScene.value),
+        MutableStateFlow(
+                mapOf(
+                    Swipe.Up to SceneFamilies.Home,
+                    Back to SceneFamilies.Home,
+                )
             )
-
-    private fun destinationScenes(backgroundScene: SceneKey): Map<UserAction, UserActionResult> {
-        return mapOf(
-            Swipe.Up to backgroundScene,
-            Back to backgroundScene,
-        )
-    }
+            .asStateFlow()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
index 5e6ee4d..3a8fe71 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.QRCodeScannerTile
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
 import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
@@ -46,6 +47,13 @@
     @StringKey(QRCodeScannerTile.TILE_SPEC)
     fun bindQRCodeScannerTile(qrCodeScannerTile: QRCodeScannerTile): QSTileImpl<*>
 
+    @Binds
+    @IntoMap
+    @StringKey(QR_CODE_SCANNER_TILE_SPEC)
+    fun provideQrCodeScannerAvailabilityInteractor(
+            impl: QRCodeScannerTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
     companion object {
         const val QR_CODE_SCANNER_TILE_SPEC = "qr_code_scanner"
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 9c8c17b..abc2b7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -113,6 +113,9 @@
 
     private boolean mSceneContainerEnabled;
 
+    @Nullable
+    private View mMediaViewPlaceHolderForScene;
+
     public QSPanel(Context context, AttributeSet attrs) {
         super(context, attrs);
         mUsingMediaPlayer = useQsMediaPlayer(context);
@@ -125,7 +128,6 @@
         setOrientation(VERTICAL);
 
         mMovableContentStartIndex = getChildCount();
-
     }
 
     void initialize(QSLogger qsLogger, boolean usingMediaPlayer) {
@@ -133,7 +135,7 @@
         mUsingMediaPlayer = usingMediaPlayer;
         mTileLayout = getOrCreateTileLayout();
 
-        if (mUsingMediaPlayer) {
+        if (mUsingMediaPlayer || SceneContainerFlag.isEnabled()) {
             mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext);
             mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
             mHorizontalLinearLayout.setVisibility(
@@ -151,6 +153,13 @@
             lp.setMarginEnd(marginSize);
             lp.gravity = Gravity.CENTER_VERTICAL;
             mHorizontalLinearLayout.addView(mHorizontalContentContainer, lp);
+            if (SceneContainerFlag.isEnabled()) {
+                int mediaHeight = mContext.getResources()
+                        .getDimensionPixelSize(R.dimen.qs_media_session_height_expanded);
+                lp = new LayoutParams(0, mediaHeight, 1);
+                mMediaViewPlaceHolderForScene = new View(mContext);
+                mHorizontalLinearLayout.addView(mMediaViewPlaceHolderForScene, lp);
+            }
 
             lp = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1);
             addView(mHorizontalLinearLayout, lp);
@@ -208,10 +217,13 @@
     private void setBrightnessViewMargin() {
         if (mBrightnessView != null) {
             MarginLayoutParams lp = (MarginLayoutParams) mBrightnessView.getLayoutParams();
+            // For Brightness Slider to extend its boundary to draw focus background
+            int offset = getResources()
+                    .getDimensionPixelSize(R.dimen.rounded_slider_boundary_offset);
             lp.topMargin = mContext.getResources()
-                    .getDimensionPixelSize(R.dimen.qs_brightness_margin_top);
+                    .getDimensionPixelSize(R.dimen.qs_brightness_margin_top) - offset;
             lp.bottomMargin = mContext.getResources()
-                    .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom);
+                    .getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom) - offset;
             mBrightnessView.setLayoutParams(lp);
         }
     }
@@ -383,6 +395,13 @@
         if (mTileLayout != null) {
             mTileLayout.updateResources();
         }
+
+        if (mMediaViewPlaceHolderForScene != null) {
+            ViewGroup.LayoutParams lp = mMediaViewPlaceHolderForScene.getLayoutParams();
+            lp.height = mContext.getResources()
+                    .getDimensionPixelSize(R.dimen.qs_media_session_height_expanded);
+            mMediaViewPlaceHolderForScene.setLayoutParams(lp);
+        }
     }
 
     protected void updatePadding() {
@@ -417,7 +436,8 @@
     }
 
     private void updateHorizontalLinearLayoutMargins() {
-        if (mUsingMediaPlayer && mHorizontalLinearLayout != null && !displayMediaMarginsOnMedia()) {
+        if ((mUsingMediaPlayer || SceneContainerFlag.isEnabled()) && mHorizontalLinearLayout != null
+                && !displayMediaMarginsOnMedia()) {
             LayoutParams lp = (LayoutParams) mHorizontalLinearLayout.getLayoutParams();
             lp.bottomMargin = Math.max(mMediaTotalBottomMargin - getPaddingBottom(), 0);
             mHorizontalLinearLayout.setLayoutParams(lp);
@@ -632,6 +652,7 @@
             // using media, the parent should always be this.
             ViewGroup newParent =
                     horizontal && mUsingMediaPlayer ? mHorizontalContentContainer : this;
+            if (SceneContainerFlag.isEnabled()) return;
             switchAllContentToParent(newParent, mTileLayout);
             reAttachMediaHost(mediaHostView, horizontal);
             if (needsDynamicRowsAndColumns()) {
@@ -647,6 +668,19 @@
     void setColumnRowLayout(boolean withMedia) {
         mTileLayout.setMinRows(withMedia ? 2 : 1);
         mTileLayout.setMaxColumns(withMedia ? 2 : 4);
+        placeTileLayoutForScene(withMedia);
+    }
+
+    protected void placeTileLayoutForScene(boolean withMedia) {
+        // The tile layout should be reparented if horizontal and we are using media. If not
+        // using media, the parent should always be this.
+        ViewGroup newParent = withMedia ? mHorizontalContentContainer : this;
+        if (mTileLayout != null && ((View) mTileLayout).getParent() != newParent) {
+            switchAllContentToParent(newParent, mTileLayout);
+        }
+        if (mHorizontalLinearLayout != null) {
+            mHorizontalLinearLayout.setVisibility(withMedia ? View.VISIBLE : View.GONE);
+        }
     }
 
     private void updateMargins(ViewGroup mediaHostView) {
@@ -699,6 +733,12 @@
         mCanCollapse = canCollapse;
     }
 
+    @Nullable
+    @VisibleForTesting
+    View getMediaPlaceholder() {
+        return mMediaViewPlaceHolderForScene;
+    }
+
     public interface QSTileLayout {
         /** */
         default void saveInstanceState(Bundle outState) {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 3b5cc61..13cedc2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -456,7 +456,7 @@
     boolean switchTileLayout(boolean force) {
         /* Whether or not the panel currently contains a media player. */
         boolean horizontal = shouldUseHorizontalLayout();
-        if (horizontal != mUsingHorizontalLayout || force) {
+        if ((!SceneContainerFlag.isEnabled() && horizontal != mUsingHorizontalLayout) || force) {
             mQSLogger.logSwitchTileLayout(horizontal, mUsingHorizontalLayout, force,
                     mView.getDumpableTag());
             mUsingHorizontalLayout = horizontal;
@@ -470,7 +470,7 @@
         return false;
     }
 
-    void setLayoutForMediaInScene() {
+    private void setLayoutForMediaInScene() {
         boolean withMedia = shouldUseHorizontalInScene();
         mView.setColumnRowLayout(withMedia);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index ea89be6..b705a03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.os.Handler;
 
-import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogModule;
 import com.android.systemui.dagger.NightDisplayListenerModule;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -61,7 +60,6 @@
  */
 @Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class},
         includes = {
-                BluetoothTileDialogModule.class,
                 MediaModule.class,
                 PanelsModule.class,
                 QSExternalModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index d457e88..fb47b40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -332,7 +332,7 @@
                 if (info.applicationInfo.isSystemApp()) {
                     final StatusBarIcon statusIcon = icon != null
                             ? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
-                            contentDescription)
+                            contentDescription, StatusBarIcon.Type.SystemIcon)
                             : null;
                     final String slot = getStatusBarIconSlotName(componentName);
                     mMainHandler.post(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt
new file mode 100644
index 0000000..00cd96c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.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.qs.panels.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import javax.inject.Inject
+
+/**
+ * Uses the [QSTileAvailabilityInteractor] from the new tiles to provide a map of availability, for
+ * the current user.
+ *
+ * This map contains every platform tile that can be constructed in the new tile infrastructure.
+ */
+@SysUISingleton
+class NewTilesAvailabilityInteractor
+@Inject
+constructor(
+        private val availabilityInteractors:
+                Map<String, @JvmSuppressWildcards QSTileAvailabilityInteractor>,
+        userRepository: UserRepository,
+) {
+    val newTilesAvailable: Flow<Map<TileSpec, Boolean>> =
+            userRepository.selectedUserInfo.map { it.id }
+                    .flatMapLatestConflated { userId ->
+                        if (availabilityInteractors.isEmpty()) {
+                            flowOf(emptyMap())
+                        } else {
+                            combine(availabilityInteractors.map { (spec, interactor) ->
+                                interactor.availability(UserHandle.of(userId)).map {
+                                    TileSpec.create(spec) to it
+                                }
+                            }) {
+                                it.toMap()
+                            }
+                        }
+                    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractor.kt
new file mode 100644
index 0000000..848688b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractor.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.systemui.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.qs.QSFactory
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import kotlinx.coroutines.flow.first
+import javax.inject.Inject
+
+/**
+ * Given a list of tiles, it determines which of those are unavailable.
+ */
+@SysUISingleton
+class TilesAvailabilityInteractor
+@Inject
+constructor(
+        private val newTilesAvailabilityInteractor: NewTilesAvailabilityInteractor,
+        private val qsFactoryImpl: QSFactory,
+        private val qsPipelineFlagsRepository: QSPipelineFlagsRepository,
+){
+    /**
+     * Checks a list of tiles and returns which are unavailable. If there's no availability
+     * interactor (or the new tiles flag is off), it will construct an instance of the tile to check
+     * its availability and destroy it. Because of this it's recommended to call this method
+     * sparingly.
+     */
+    suspend fun getUnavailableTiles(platformTilesToCheck: Iterable<TileSpec>): Set<TileSpec> {
+        check(platformTilesToCheck.all { it is TileSpec.PlatformTileSpec })
+        val newTilesAvailability = if (qsPipelineFlagsRepository.tilesEnabled) {
+            newTilesAvailabilityInteractor.newTilesAvailable.first()
+        } else {
+            emptyMap()
+        }
+        return (platformTilesToCheck.minus(newTilesAvailability.keys).filterNot {
+            val tile = qsFactoryImpl.createTile(it.spec)
+            (tile?.isAvailable ?: false).also {
+                tile?.destroy()
+            }
+        } + newTilesAvailability.filterNot { it.value }.keys).toSet()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
index d600767..9233e76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs.panels.ui.compose
 
-import androidx.compose.foundation.border
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -40,6 +39,13 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.PathEffect
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.addOutline
+import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.Dp
@@ -268,10 +274,9 @@
     private fun CurrentTilesContainer(content: @Composable () -> Unit) {
         Box(
             Modifier.fillMaxWidth()
-                .border(
-                    width = 1.dp,
-                    color = MaterialTheme.colorScheme.onBackground,
-                    shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))
+                .dashedBorder(
+                    color = MaterialTheme.colorScheme.onBackground.copy(alpha = .5f),
+                    shape = Dimensions.ContainerShape,
                 )
                 .padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
         ) {
@@ -284,9 +289,9 @@
         Box(
             Modifier.fillMaxWidth()
                 .background(
-                    color = MaterialTheme.colorScheme.surfaceVariant,
+                    color = MaterialTheme.colorScheme.background,
                     alpha = { 1f },
-                    shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius))
+                    shape = Dimensions.ContainerShape,
                 )
                 .padding(dimensionResource(R.dimen.qs_tile_margin_vertical))
         ) {
@@ -305,4 +310,27 @@
             item(span = { GridItemSpan(maxCurrentLineSpan) }) { Spacer(Modifier) }
         }
     }
+
+    private fun Modifier.dashedBorder(
+        color: Color,
+        shape: Shape,
+    ): Modifier {
+        return this.drawWithContent {
+            val outline = shape.createOutline(size, layoutDirection, this)
+            val path = Path()
+            path.addOutline(outline)
+            val stroke =
+                Stroke(
+                    width = 1.dp.toPx(),
+                    pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 10f))
+                )
+            this.drawContent()
+            drawPath(path = path, style = stroke, color = color)
+        }
+    }
+
+    private object Dimensions {
+        // Corner radius is half the height of a tile + padding
+        val ContainerShape = RoundedCornerShape(48.dp)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index f776bf0..bbb98d3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalFoundationApi::class)
+
 package com.android.systemui.qs.panels.ui.compose
 
 import android.graphics.drawable.Animatable
+import android.service.quicksettings.Tile.STATE_ACTIVE
+import android.service.quicksettings.Tile.STATE_INACTIVE
 import android.text.TextUtils
 import androidx.appcompat.content.res.AppCompatResources
 import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
@@ -25,17 +29,17 @@
 import androidx.compose.animation.graphics.vector.AnimatedImageVector
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
 import androidx.compose.foundation.basicMarquee
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Arrangement.spacedBy
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
@@ -44,11 +48,7 @@
 import androidx.compose.foundation.lazy.grid.LazyGridScope
 import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
 import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.filled.Remove
-import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -65,7 +65,6 @@
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.onClick
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.semantics.stateDescription
@@ -75,14 +74,13 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Expandable
-import com.android.compose.theme.colorAttr
+import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.common.ui.compose.load
-import com.android.systemui.qs.panels.ui.viewmodel.ActiveTileColorAttributes
+import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.TileColorAttributes
 import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.toUiState
@@ -90,13 +88,14 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.res.R
+import java.util.function.Supplier
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.mapLatest
 
 object TileType
 
-@OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
+@OptIn(ExperimentalCoroutinesApi::class)
 @Composable
 fun Tile(
     tile: TileViewModel,
@@ -108,46 +107,145 @@
         tile.state
             .mapLatest { it.toUiState() }
             .collectAsStateWithLifecycle(tile.currentState.toUiState())
-    val context = LocalContext.current
+    val colors = TileDefaults.getColorForState(state.state)
 
-    Expandable(
-        color = colorAttr(state.colors.background),
-        shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)),
+    TileContainer(
+        colors = colors,
+        showLabels = showLabels,
+        label = state.label.toString(),
+        iconOnly = iconOnly,
+        onClick = tile::onClick,
+        onLongClick = tile::onLongClick,
+        modifier = modifier,
     ) {
-        Row(
-            modifier =
-                modifier
-                    .combinedClickable(
-                        onClick = { tile.onClick(it) },
-                        onLongClick = { tile.onLongClick(it) }
-                    )
-                    .tileModifier(state.colors),
-            verticalAlignment = Alignment.CenterVertically,
-            horizontalArrangement = tileHorizontalArrangement(iconOnly),
-        ) {
-            val icon =
-                remember(state.icon) {
-                    state.icon.get().let {
-                        if (it is QSTileImpl.ResourceIcon) {
-                            Icon.Resource(it.resId, null)
-                        } else {
-                            Icon.Loaded(it.getDrawable(context), null)
-                        }
-                    }
-                }
-            TileContent(
+        val icon = getTileIcon(icon = state.icon)
+        if (iconOnly) {
+            TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center))
+        } else {
+            LargeTileContent(
                 label = state.label.toString(),
                 secondaryLabel = state.secondaryLabel.toString(),
                 icon = icon,
-                colors = state.colors,
-                iconOnly = iconOnly,
-                showLabels = showLabels,
+                colors = colors,
+                onClick = tile::onSecondaryClick,
+                onLongClick = tile::onLongClick,
             )
         }
     }
 }
 
 @Composable
+private fun TileContainer(
+    colors: TileColors,
+    showLabels: Boolean,
+    label: String,
+    iconOnly: Boolean,
+    clickEnabled: Boolean = true,
+    onClick: (Expandable) -> Unit = {},
+    onLongClick: (Expandable) -> Unit = {},
+    modifier: Modifier = Modifier,
+    content: @Composable BoxScope.() -> Unit,
+) {
+    Column(
+        horizontalAlignment = Alignment.CenterHorizontally,
+        verticalArrangement =
+            spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin), Alignment.Top),
+        modifier = modifier,
+    ) {
+        val backgroundColor =
+            if (iconOnly) {
+                colors.iconBackground
+            } else {
+                colors.background
+            }
+        Expandable(
+            color = backgroundColor,
+            shape = TileDefaults.TileShape,
+            modifier =
+                Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+                    .clip(TileDefaults.TileShape)
+        ) {
+            Box(
+                modifier =
+                    Modifier.fillMaxSize()
+                        .combinedClickable(
+                            enabled = clickEnabled,
+                            onClick = { onClick(it) },
+                            onLongClick = { onLongClick(it) }
+                        )
+                        .tilePadding(),
+            ) {
+                content()
+            }
+        }
+
+        if (showLabels && iconOnly) {
+            Text(
+                label,
+                maxLines = 2,
+                color = colors.label,
+                overflow = TextOverflow.Ellipsis,
+                textAlign = TextAlign.Center,
+            )
+        }
+    }
+}
+
+@Composable
+private fun LargeTileContent(
+    label: String,
+    secondaryLabel: String?,
+    icon: Icon,
+    colors: TileColors,
+    clickEnabled: Boolean = true,
+    onClick: (Expandable) -> Unit = {},
+    onLongClick: (Expandable) -> Unit = {},
+) {
+    Row(
+        verticalAlignment = Alignment.CenterVertically,
+        horizontalArrangement = tileHorizontalArrangement()
+    ) {
+        Expandable(
+            color = colors.iconBackground,
+            shape = TileDefaults.TileShape,
+            modifier = Modifier.fillMaxHeight().aspectRatio(1f)
+        ) {
+            Box(
+                modifier =
+                    Modifier.fillMaxSize()
+                        .clip(TileDefaults.TileShape)
+                        .combinedClickable(
+                            enabled = clickEnabled,
+                            onClick = { onClick(it) },
+                            onLongClick = { onLongClick(it) }
+                        )
+            ) {
+                TileIcon(
+                    icon = icon,
+                    color = colors.icon,
+                    modifier = Modifier.align(Alignment.Center)
+                )
+            }
+        }
+
+        Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
+            Text(
+                label,
+                color = colors.label,
+                modifier = Modifier.basicMarquee(),
+            )
+            if (!TextUtils.isEmpty(secondaryLabel)) {
+                Text(
+                    secondaryLabel ?: "",
+                    color = colors.secondaryLabel,
+                    modifier = Modifier.basicMarquee(),
+                )
+            }
+        }
+    }
+}
+
+@Composable
 fun TileLazyGrid(
     modifier: Modifier = Modifier,
     columns: GridCells,
@@ -245,41 +343,19 @@
                 ""
             }
 
-        Box(
+        val iconOnly = isIconOnly(viewModel.tileSpec)
+        val tileHeight = tileHeight(iconOnly && showLabels)
+        EditTile(
+            tileViewModel = viewModel,
+            iconOnly = iconOnly,
+            showLabels = showLabels,
+            clickEnabled = canClick,
+            onClick = { onClick.invoke(viewModel.tileSpec) },
             modifier =
-                Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) }
-                    .animateItem()
-                    .semantics {
-                        onClick(onClickActionName) { false }
-                        this.stateDescription = stateDescription
-                    }
-        ) {
-            val iconOnly = isIconOnly(viewModel.tileSpec)
-            val tileHeight = tileHeight(iconOnly && showLabels)
-            EditTile(
-                tileViewModel = viewModel,
-                iconOnly = iconOnly,
-                showLabels = showLabels,
-                modifier = Modifier.height(tileHeight)
-            )
-            if (canClick) {
-                Badge(clickAction, Modifier.align(Alignment.TopEnd))
-            }
-        }
-    }
-}
-
-@Composable
-fun Badge(action: ClickAction, modifier: Modifier = Modifier) {
-    Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) {
-        Icon(
-            imageVector =
-                when (action) {
-                    ClickAction.ADD -> Icons.Filled.Add
-                    ClickAction.REMOVE -> Icons.Filled.Remove
-                },
-            "",
-            tint = Color.Black,
+                Modifier.height(tileHeight).animateItem().semantics {
+                    onClick(onClickActionName) { false }
+                    this.stateDescription = stateDescription
+                }
         )
     }
 }
@@ -289,25 +365,40 @@
     tileViewModel: EditTileViewModel,
     iconOnly: Boolean,
     showLabels: Boolean,
+    clickEnabled: Boolean,
+    onClick: () -> Unit,
     modifier: Modifier = Modifier,
 ) {
     val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
-    val colors = ActiveTileColorAttributes
+    val colors = TileDefaults.inactiveTileColors()
 
-    Row(
-        modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label },
-        verticalAlignment = Alignment.CenterVertically,
-        horizontalArrangement = tileHorizontalArrangement(iconOnly)
+    TileContainer(
+        colors = colors,
+        showLabels = showLabels,
+        label = label,
+        iconOnly = iconOnly,
+        clickEnabled = clickEnabled,
+        onClick = { onClick() },
+        onLongClick = { onClick() },
+        modifier = modifier,
     ) {
-        TileContent(
-            label = label,
-            secondaryLabel = tileViewModel.appName?.load(),
-            colors = colors,
-            icon = tileViewModel.icon,
-            iconOnly = iconOnly,
-            showLabels = showLabels,
-            animateIconToEnd = true,
-        )
+        if (iconOnly) {
+            TileIcon(
+                icon = tileViewModel.icon,
+                color = colors.icon,
+                modifier = Modifier.align(Alignment.Center)
+            )
+        } else {
+            LargeTileContent(
+                label = label,
+                secondaryLabel = tileViewModel.appName?.load(),
+                icon = tileViewModel.icon,
+                colors = colors,
+                clickEnabled = clickEnabled,
+                onClick = { onClick() },
+                onLongClick = { onClick() },
+            )
+        }
     }
 }
 
@@ -316,14 +407,27 @@
     REMOVE,
 }
 
+@Composable
+private fun getTileIcon(icon: Supplier<QSTile.Icon>): Icon {
+    val context = LocalContext.current
+    return icon.get().let {
+        if (it is QSTileImpl.ResourceIcon) {
+            Icon.Resource(it.resId, null)
+        } else {
+            Icon.Loaded(it.getDrawable(context), null)
+        }
+    }
+}
+
 @OptIn(ExperimentalAnimationGraphicsApi::class)
 @Composable
 private fun TileIcon(
     icon: Icon,
     color: Color,
     animateToEnd: Boolean = false,
+    modifier: Modifier = Modifier,
 ) {
-    val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
+    val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
     val context = LocalContext.current
     val loadedDrawable =
         remember(icon, context) {
@@ -336,7 +440,7 @@
         Icon(
             icon = icon,
             tint = color,
-            modifier = modifier,
+            modifier = iconModifier,
         )
     } else if (icon is Icon.Resource) {
         val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
@@ -355,88 +459,81 @@
             painter = painter,
             contentDescription = null,
             colorFilter = ColorFilter.tint(color = color),
-            modifier = modifier
+            modifier = iconModifier
         )
     }
 }
 
 @Composable
-private fun Modifier.tileModifier(colors: TileColorAttributes): Modifier {
-    return fillMaxWidth()
-        .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)))
-        .background(colorAttr(colors.background))
-        .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin))
+private fun Modifier.tilePadding(): Modifier {
+    return padding(dimensionResource(id = R.dimen.qs_label_container_margin))
 }
 
 @Composable
-private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal {
-    val horizontalAlignment =
-        if (iconOnly) {
-            Alignment.CenterHorizontally
-        } else {
-            Alignment.Start
-        }
+private fun tileHorizontalArrangement(): Arrangement.Horizontal {
     return spacedBy(
         space = dimensionResource(id = R.dimen.qs_label_container_margin),
-        alignment = horizontalAlignment
+        alignment = Alignment.Start
     )
 }
 
 @Composable
-private fun TileContent(
-    label: String,
-    secondaryLabel: String?,
-    icon: Icon,
-    colors: TileColorAttributes,
-    iconOnly: Boolean,
-    showLabels: Boolean = false,
-    animateIconToEnd: Boolean = false,
-) {
-    Column(
-        horizontalAlignment = Alignment.CenterHorizontally,
-        verticalArrangement = Arrangement.Center,
-        modifier = Modifier.fillMaxHeight()
-    ) {
-        TileIcon(icon, colorAttr(colors.icon), animateIconToEnd)
-
-        if (iconOnly && showLabels) {
-            Text(
-                label,
-                maxLines = 2,
-                color = colorAttr(colors.label),
-                overflow = TextOverflow.Ellipsis,
-                textAlign = TextAlign.Center,
-            )
-        }
-    }
-
-    if (!iconOnly) {
-        Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
-            Text(
-                label,
-                color = colorAttr(colors.label),
-                modifier = Modifier.basicMarquee(),
-            )
-            if (!TextUtils.isEmpty(secondaryLabel)) {
-                Text(
-                    secondaryLabel ?: "",
-                    color = colorAttr(colors.secondaryLabel),
-                    modifier = Modifier.basicMarquee(),
-                )
-            }
-        }
-    }
-}
-
-@Composable
 fun tileHeight(iconWithLabel: Boolean = false): Dp {
     return if (iconWithLabel) {
-        TileDimensions.IconTileWithLabelHeight
+        TileDefaults.IconTileWithLabelHeight
     } else {
         dimensionResource(id = R.dimen.qs_tile_height)
     }
 }
 
-private object TileDimensions {
-    val IconTileWithLabelHeight = 100.dp
+private data class TileColors(
+    val background: Color,
+    val iconBackground: Color,
+    val label: Color,
+    val secondaryLabel: Color,
+    val icon: Color,
+)
+
+private object TileDefaults {
+    val TileShape = CircleShape
+    val IconTileWithLabelHeight = 140.dp
+
+    @Composable
+    fun activeTileColors(): TileColors =
+        TileColors(
+            background = MaterialTheme.colorScheme.surfaceVariant,
+            iconBackground = MaterialTheme.colorScheme.primary,
+            label = MaterialTheme.colorScheme.onSurfaceVariant,
+            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
+            icon = MaterialTheme.colorScheme.onPrimary,
+        )
+
+    @Composable
+    fun inactiveTileColors(): TileColors =
+        TileColors(
+            background = MaterialTheme.colorScheme.surfaceVariant,
+            iconBackground = MaterialTheme.colorScheme.surfaceVariant,
+            label = MaterialTheme.colorScheme.onSurfaceVariant,
+            secondaryLabel = MaterialTheme.colorScheme.onSurfaceVariant,
+            icon = MaterialTheme.colorScheme.onSurfaceVariant,
+        )
+
+    @Composable
+    fun unavailableTileColors(): TileColors =
+        TileColors(
+            background = MaterialTheme.colorScheme.surface,
+            iconBackground = MaterialTheme.colorScheme.surface,
+            label = MaterialTheme.colorScheme.onSurface,
+            secondaryLabel = MaterialTheme.colorScheme.onSurface,
+            icon = MaterialTheme.colorScheme.onSurface,
+        )
+
+    @Composable
+    fun getColorForState(state: Int): TileColors {
+        return when (state) {
+            STATE_ACTIVE -> activeTileColors()
+            STATE_INACTIVE -> inactiveTileColors()
+            else -> unavailableTileColors()
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 5b4186c..19b8c66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -20,14 +20,13 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.qs.panels.domain.interactor.EditTilesListInteractor
 import com.android.systemui.qs.panels.domain.interactor.GridLayoutTypeInteractor
+import com.android.systemui.qs.panels.domain.interactor.TilesAvailabilityInteractor
 import com.android.systemui.qs.panels.shared.model.GridLayoutType
 import com.android.systemui.qs.panels.ui.compose.GridLayout
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
 import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import javax.inject.Inject
-import javax.inject.Named
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -38,19 +37,22 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
+import javax.inject.Inject
+import javax.inject.Named
 
 @SysUISingleton
 @OptIn(ExperimentalCoroutinesApi::class)
 class EditModeViewModel
 @Inject
 constructor(
-    private val editTilesListInteractor: EditTilesListInteractor,
-    private val currentTilesInteractor: CurrentTilesInteractor,
-    private val minTilesInteractor: MinimumTilesInteractor,
-    @Named("Default") private val defaultGridLayout: GridLayout,
-    @Application private val applicationScope: CoroutineScope,
-    gridLayoutTypeInteractor: GridLayoutTypeInteractor,
-    gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>,
+        private val editTilesListInteractor: EditTilesListInteractor,
+        private val currentTilesInteractor: CurrentTilesInteractor,
+        private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
+        private val minTilesInteractor: MinimumTilesInteractor,
+        @Named("Default") private val defaultGridLayout: GridLayout,
+        @Application private val applicationScope: CoroutineScope,
+        gridLayoutTypeInteractor: GridLayoutTypeInteractor,
+        gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>,
 ) {
     private val _isEditing = MutableStateFlow(false)
 
@@ -82,11 +84,19 @@
      *   Quick Settings.
      * * The tiles that are not current will preserve their relative order even when the current
      *   tiles change.
+     * * Tiles that are not available will be filtered out. None of them can be current (as they
+     *   cannot be created), and they won't be able to be added.
      */
     val tiles =
         isEditing.flatMapLatest {
             if (it) {
                 val editTilesData = editTilesListInteractor.getTilesToEdit()
+                // Query only the non current platform tiles, as any current tile is clearly
+                // available
+                val unavailable = tilesAvailabilityInteractor.getUnavailableTiles(
+                        editTilesData.stockTiles.map { it.tileSpec }
+                                .minus(currentTilesInteractor.currentTilesSpecs.toSet())
+                )
                 currentTilesInteractor.currentTiles.map { tiles ->
                     val currentSpecs = tiles.map { it.spec }
                     val canRemoveTiles = currentSpecs.size > minimumTiles
@@ -95,27 +105,29 @@
                     val currentTiles = currentSpecs.map { allTilesMap.get(it) }.filterNotNull()
                     val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs }
 
-                    (currentTiles + nonCurrentTiles).map {
-                        val current = it.tileSpec in currentSpecs
-                        val availableActions = buildSet {
-                            if (current) {
-                                add(AvailableEditActions.MOVE)
-                                if (canRemoveTiles) {
-                                    add(AvailableEditActions.REMOVE)
+                    (currentTiles + nonCurrentTiles)
+                            .filterNot { it.tileSpec in unavailable }
+                            .map {
+                                val current = it.tileSpec in currentSpecs
+                                val availableActions = buildSet {
+                                    if (current) {
+                                        add(AvailableEditActions.MOVE)
+                                        if (canRemoveTiles) {
+                                            add(AvailableEditActions.REMOVE)
+                                        }
+                                    } else {
+                                        add(AvailableEditActions.ADD)
+                                    }
                                 }
-                            } else {
-                                add(AvailableEditActions.ADD)
+                                EditTileViewModel(
+                                        it.tileSpec,
+                                        it.icon,
+                                        it.label,
+                                        it.appName,
+                                        current,
+                                        availableActions
+                                )
                             }
-                        }
-                        EditTileViewModel(
-                            it.tileSpec,
-                            it.icon,
-                            it.label,
-                            it.appName,
-                            current,
-                            availableActions
-                        )
-                    }
                 }
             } else {
                 emptyFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileColorAttributes.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileColorAttributes.kt
deleted file mode 100644
index 1290bf3..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileColorAttributes.kt
+++ /dev/null
@@ -1,61 +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.qs.panels.ui.viewmodel
-
-import android.service.quicksettings.Tile
-import androidx.annotation.AttrRes
-import com.android.systemui.plugins.qs.QSTile
-import com.android.systemui.res.R
-
-data class TileColorAttributes(
-    @AttrRes val background: Int = 0,
-    @AttrRes val label: Int = 0,
-    @AttrRes val secondaryLabel: Int = 0,
-    @AttrRes val icon: Int = 0,
-)
-
-val ActiveTileColorAttributes =
-    TileColorAttributes(
-        background = R.attr.shadeActive,
-        label = R.attr.onShadeActive,
-        secondaryLabel = R.attr.onShadeActiveVariant,
-        icon = R.attr.onShadeActive,
-    )
-
-val InactiveTileColorAttributes =
-    TileColorAttributes(
-        background = R.attr.shadeInactive,
-        label = R.attr.onShadeInactive,
-        secondaryLabel = R.attr.onShadeInactiveVariant,
-        icon = R.attr.onShadeInactiveVariant,
-    )
-
-val UnavailableTileColorAttributes =
-    TileColorAttributes(
-        background = R.attr.shadeDisabled,
-        label = R.attr.outline,
-        secondaryLabel = R.attr.outline,
-        icon = R.attr.outline,
-    )
-
-fun QSTile.State.colors(): TileColorAttributes =
-    when (state) {
-        Tile.STATE_UNAVAILABLE -> UnavailableTileColorAttributes
-        Tile.STATE_ACTIVE -> ActiveTileColorAttributes
-        Tile.STATE_INACTIVE -> InactiveTileColorAttributes
-        else -> TileColorAttributes()
-    }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 58d07c3..578a292 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -22,7 +22,7 @@
 data class TileUiState(
     val label: CharSequence,
     val secondaryLabel: CharSequence,
-    val colors: TileColorAttributes,
+    val state: Int,
     val icon: Supplier<QSTile.Icon>,
 )
 
@@ -30,7 +30,7 @@
     return TileUiState(
         label ?: "",
         secondaryLabel ?: "",
-        colors(),
+        state,
         icon?.let { Supplier { icon } } ?: iconSupplier,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
index a6cfa75..7505b90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
@@ -48,6 +48,10 @@
         tile.longClick(expandable)
     }
 
+    fun onSecondaryClick(expandable: Expandable?) {
+        tile.secondaryClick(expandable)
+    }
+
     fun startListening(token: Any) = tile.setListening(token, true)
 
     fun stopListening(token: Any) = tile.setListening(token, false)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
index b5bef9f..88f7169 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/CastAutoAddable.kt
@@ -41,12 +41,7 @@
 
     override fun ProducerScope<AutoAddSignal>.getCallback(): CastController.Callback {
         return CastController.Callback {
-            val isCasting =
-                controller.castDevices.any {
-                    it.state == CastController.CastDevice.STATE_CONNECTED ||
-                        it.state == CastController.CastDevice.STATE_CONNECTING
-                }
-            if (isCasting) {
+            if (controller.castDevices.any { it.isCasting }) {
                 sendAdd()
             }
         }
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 1143c30..44c846b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -19,6 +19,7 @@
 import android.animation.ArgbEvaluator
 import android.animation.PropertyValuesHolder
 import android.animation.ValueAnimator
+import android.annotation.SuppressLint
 import android.content.Context
 import android.content.res.ColorStateList
 import android.content.res.Configuration
@@ -37,27 +38,31 @@
 import android.util.TypedValue
 import android.view.Gravity
 import android.view.LayoutInflater
+import android.view.MotionEvent
 import android.view.View
+import android.view.ViewConfiguration
 import android.view.ViewGroup
 import android.view.accessibility.AccessibilityEvent
 import android.view.accessibility.AccessibilityNodeInfo
+import android.view.animation.AccelerateDecelerateInterpolator
 import android.widget.Button
 import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.Switch
 import android.widget.TextView
 import androidx.annotation.VisibleForTesting
+import androidx.core.animation.doOnCancel
+import androidx.core.animation.doOnEnd
+import androidx.core.animation.doOnStart
 import androidx.core.graphics.drawable.updateBounds
 import com.android.app.tracing.traceSection
 import com.android.settingslib.Utils
 import com.android.systemui.Flags
-import com.android.systemui.Flags.quickSettingsVisualHapticsLongpress
 import com.android.systemui.FontSizeUtils
 import com.android.systemui.animation.Expandable
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
 import com.android.systemui.haptics.qs.QSLongPressEffect
-import com.android.systemui.haptics.qs.QSLongPressEffectViewBinder
 import com.android.systemui.plugins.qs.QSIconView
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.qs.QSTile.AdapterState
@@ -65,11 +70,13 @@
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
 import com.android.systemui.res.R
-import kotlinx.coroutines.DisposableHandle
 import java.util.Objects
 
 private const val TAG = "QSTileViewImpl"
-open class QSTileViewImpl @JvmOverloads constructor(
+
+open class QSTileViewImpl
+@JvmOverloads
+constructor(
     context: Context,
     private val collapsed: Boolean = false,
     private val longPressEffect: QSLongPressEffect? = null,
@@ -83,12 +90,9 @@
         private const val CHEVRON_NAME = "chevron"
         private const val OVERLAY_NAME = "overlay"
         const val UNAVAILABLE_ALPHA = 0.3f
-        @VisibleForTesting
-        internal const val TILE_STATE_RES_PREFIX = "tile_states_"
-        @VisibleForTesting
-        internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f
-        @VisibleForTesting
-        internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f
+        @VisibleForTesting internal const val TILE_STATE_RES_PREFIX = "tile_states_"
+        @VisibleForTesting internal const val LONG_PRESS_EFFECT_WIDTH_SCALE = 1.1f
+        @VisibleForTesting internal const val LONG_PRESS_EFFECT_HEIGHT_SCALE = 1.2f
     }
 
     private val icon: QSIconViewImpl = QSIconViewImpl(context)
@@ -116,22 +120,25 @@
     private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive)
     private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled)
 
-    private val overlayColorActive = Utils.applyAlpha(
-        /* alpha= */ 0.11f,
-        Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive))
-    private val overlayColorInactive = Utils.applyAlpha(
-        /* alpha= */ 0.08f,
-        Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive))
+    private val overlayColorActive =
+        Utils.applyAlpha(
+            /* alpha= */ 0.11f,
+            Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
+        )
+    private val overlayColorInactive =
+        Utils.applyAlpha(
+            /* alpha= */ 0.08f,
+            Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
+        )
 
     private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
     private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
-    private val colorLabelUnavailable =
-        Utils.getColorAttrDefaultColor(context, R.attr.outline)
+    private val colorLabelUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.outline)
 
     private val colorSecondaryLabelActive =
         Utils.getColorAttrDefaultColor(context, R.attr.onShadeActiveVariant)
     private val colorSecondaryLabelInactive =
-            Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant)
+        Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant)
     private val colorSecondaryLabelUnavailable =
         Utils.getColorAttrDefaultColor(context, R.attr.outline)
 
@@ -143,9 +150,7 @@
     private lateinit var chevronView: ImageView
     private var mQsLogger: QSLogger? = null
 
-    /**
-     * Controls if tile background is set to a [RippleDrawable] see [setClickable]
-     */
+    /** Controls if tile background is set to a [RippleDrawable] see [setClickable] */
     protected var showRippleEffect = true
 
     private lateinit var qsTileBackground: RippleDrawable
@@ -157,20 +162,21 @@
     private var backgroundColor: Int = 0
     private var backgroundOverlayColor: Int = 0
 
-    private val singleAnimator: ValueAnimator = ValueAnimator().apply {
-        setDuration(QS_ANIM_LENGTH)
-        addUpdateListener { animation ->
-            setAllColors(
-                // These casts will throw an exception if some property is missing. We should
-                // always have all properties.
-                animation.getAnimatedValue(BACKGROUND_NAME) as Int,
-                animation.getAnimatedValue(LABEL_NAME) as Int,
-                animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
-                animation.getAnimatedValue(CHEVRON_NAME) as Int,
-                animation.getAnimatedValue(OVERLAY_NAME) as Int,
-            )
+    private val singleAnimator: ValueAnimator =
+        ValueAnimator().apply {
+            setDuration(QS_ANIM_LENGTH)
+            addUpdateListener { animation ->
+                setAllColors(
+                    // These casts will throw an exception if some property is missing. We should
+                    // always have all properties.
+                    animation.getAnimatedValue(BACKGROUND_NAME) as Int,
+                    animation.getAnimatedValue(LABEL_NAME) as Int,
+                    animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
+                    animation.getAnimatedValue(CHEVRON_NAME) as Int,
+                    animation.getAnimatedValue(OVERLAY_NAME) as Int,
+                )
+            }
         }
-    }
 
     private var accessibilityClass: String? = null
     private var stateDescriptionDeltas: CharSequence? = null
@@ -178,32 +184,37 @@
     private var tileState = false
     private var lastState = INVALID
     private var lastIconTint = 0
-    private val launchableViewDelegate = LaunchableViewDelegate(
-        this,
-        superSetVisibility = { super.setVisibility(it) },
-    )
+    private val launchableViewDelegate =
+        LaunchableViewDelegate(
+            this,
+            superSetVisibility = { super.setVisibility(it) },
+        )
     private var lastDisabledByPolicy = false
 
     private val locInScreen = IntArray(2)
 
     /** Visuo-haptic long-press effects */
+    private var longPressEffectAnimator: ValueAnimator? = null
     var haveLongPressPropertiesBeenReset = true
         private set
+
     private var paddingForLaunch = Rect()
     private var initialLongPressProperties: QSLongPressProperties? = null
     private var finalLongPressProperties: QSLongPressProperties? = null
     private val colorEvaluator = ArgbEvaluator.getInstance()
     val isLongPressEffectInitialized: Boolean
         get() = longPressEffect?.hasInitialized == true
-    private var longPressEffectHandle: DisposableHandle? = null
-    val isLongPressEffectBound: Boolean
-        get() = longPressEffectHandle != null
+
+    val areLongPressEffectPropertiesSet: Boolean
+        get() = initialLongPressProperties != null && finalLongPressProperties != null
 
     init {
         val typedValue = TypedValue()
         if (!getContext().theme.resolveAttribute(R.attr.isQsTheme, typedValue, true)) {
-            throw IllegalStateException("QSViewImpl must be inflated with a theme that contains " +
-                    "Theme.SystemUI.QuickSettings")
+            throw IllegalStateException(
+                "QSViewImpl must be inflated with a theme that contains " +
+                    "Theme.SystemUI.QuickSettings"
+            )
         }
         setId(generateViewId())
         orientation = LinearLayout.HORIZONTAL
@@ -261,13 +272,9 @@
         setPaddingRelative(startPadding, padding, padding, padding)
 
         val labelMargin = resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
-        (labelContainer.layoutParams as MarginLayoutParams).apply {
-            marginStart = labelMargin
-        }
+        (labelContainer.layoutParams as MarginLayoutParams).apply { marginStart = labelMargin }
 
-        (sideView.layoutParams as MarginLayoutParams).apply {
-            marginStart = labelMargin
-        }
+        (sideView.layoutParams as MarginLayoutParams).apply { marginStart = labelMargin }
         (chevronView.layoutParams as MarginLayoutParams).apply {
             height = iconSize
             width = iconSize
@@ -285,8 +292,9 @@
     }
 
     private fun createAndAddLabels() {
-        labelContainer = LayoutInflater.from(context)
-                .inflate(R.layout.qs_tile_label, this, false) as IgnorableChildLinearLayout
+        labelContainer =
+            LayoutInflater.from(context).inflate(R.layout.qs_tile_label, this, false)
+                as IgnorableChildLinearLayout
         label = labelContainer.requireViewById(R.id.tile_label)
         secondaryLabel = labelContainer.requireViewById(R.id.app_label)
         if (collapsed) {
@@ -304,8 +312,9 @@
     }
 
     private fun createAndAddSideView() {
-        sideView = LayoutInflater.from(context)
-                .inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
+        sideView =
+            LayoutInflater.from(context).inflate(R.layout.qs_tile_side_icon, this, false)
+                as ViewGroup
         customDrawableView = sideView.requireViewById(R.id.customDrawable)
         chevronView = sideView.requireViewById(R.id.chevron)
         setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE))
@@ -313,11 +322,12 @@
     }
 
     private fun createTileBackground(): Drawable {
-        qsTileBackground = if (Flags.qsTileFocusState()) {
-            mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable
-        } else {
-            mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
-        }
+        qsTileBackground =
+            if (Flags.qsTileFocusState()) {
+                mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable
+            } else {
+                mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+            }
         qsTileFocusBackground = mContext.getDrawable(R.drawable.qs_tile_focused_background)!!
         backgroundDrawable =
             qsTileBackground.findDrawableByLayerId(R.id.background) as LayerDrawable
@@ -332,21 +342,29 @@
     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
         super.onLayout(changed, l, t, r, b)
         updateHeight()
-        maybeUpdateLongPressEffectDimensions()
+        maybeUpdateLongPressEffectWidth(measuredWidth.toFloat())
     }
 
-    private fun maybeUpdateLongPressEffectDimensions() {
+    private fun maybeUpdateLongPressEffectWidth(width: Float) {
         if (!isLongClickable || longPressEffect == null) return
 
-        val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
-            heightOverride
-        } else {
-            measuredHeight
-        }
-        initialLongPressProperties?.height = actualHeight.toFloat()
-        initialLongPressProperties?.width = measuredWidth.toFloat()
-        finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * actualHeight
-        finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * measuredWidth
+        initialLongPressProperties?.width = width
+        finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * width
+
+        val deltaW = (LONG_PRESS_EFFECT_WIDTH_SCALE - 1f) * width
+        paddingForLaunch.left = -deltaW.toInt() / 2
+        paddingForLaunch.right = deltaW.toInt() / 2
+    }
+
+    private fun maybeUpdateLongPressEffectHeight(height: Float) {
+        if (!isLongClickable || longPressEffect == null) return
+
+        initialLongPressProperties?.height = height
+        finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * height
+
+        val deltaH = (LONG_PRESS_EFFECT_HEIGHT_SCALE - 1f) * height
+        paddingForLaunch.top = -deltaH.toInt() / 2
+        paddingForLaunch.bottom = deltaH.toInt() / 2
     }
 
     override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
@@ -360,6 +378,7 @@
             }
         }
     }
+
     private fun updateHeight() {
         // TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
         //  launch animation.
@@ -368,16 +387,18 @@
             // we must do it here
             resetLongPressEffectProperties()
         }
-        val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
-            heightOverride
-        } else {
-            measuredHeight
-        }
+        val actualHeight =
+            if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
+                heightOverride
+            } else {
+                measuredHeight
+            }
         // Limit how much we affect the height, so we don't have rounding artifacts when the tile
         // is too short.
         val constrainedSquishiness = constrainSquishiness(squishinessFraction)
         bottom = top + (actualHeight * constrainedSquishiness).toInt()
         scrollY = (actualHeight - height) / 2
+        maybeUpdateLongPressEffectHeight(actualHeight.toFloat())
     }
 
     override fun updateAccessibilityOrder(previousView: View?): View {
@@ -395,22 +416,79 @@
 
     override fun init(tile: QSTile) {
         val expandable = Expandable.fromView(this)
-        init(
+        if (longPressEffect != null) {
+            isHapticFeedbackEnabled = false
+            longPressEffect.qsTile = tile
+            longPressEffect.expandable = expandable
+            initLongPressEffectCallback()
+            init(
+                { _: View -> longPressEffect.onTileClick() },
+                null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
+            )
+        } else {
+            init(
                 { _: View? -> tile.click(expandable) },
                 { _: View? ->
                     tile.longClick(expandable)
                     true
-                }
-        )
-        if (quickSettingsVisualHapticsLongpress()) {
-            isHapticFeedbackEnabled = false // Haptics will be handled by the [QSLongPressEffect]
+                },
+            )
         }
     }
 
-    private fun init(
-        click: OnClickListener?,
-        longClick: OnLongClickListener?
-    ) {
+    private fun initLongPressEffectCallback() {
+        longPressEffect?.callback =
+            object : QSLongPressEffect.Callback {
+
+                override fun onResetProperties() {
+                    resetLongPressEffectProperties()
+                }
+
+                override fun onEffectFinishedReversing() {
+                    // The long-press effect properties finished at the same starting point.
+                    // This is the same as if the properties were reset
+                    haveLongPressPropertiesBeenReset = true
+                }
+
+                override fun onStartAnimator() {
+                    if (longPressEffectAnimator?.isRunning != true) {
+                        longPressEffectAnimator =
+                            ValueAnimator.ofFloat(0f, 1f).apply {
+                                this.duration = longPressEffect?.effectDuration?.toLong() ?: 0L
+                                interpolator = AccelerateDecelerateInterpolator()
+
+                                doOnStart { longPressEffect?.handleAnimationStart() }
+                                addUpdateListener {
+                                    val value = animatedValue as Float
+                                    if (value == 0f) {
+                                        bringToFront()
+                                    } else {
+                                        updateLongPressEffectProperties(value)
+                                    }
+                                }
+                                doOnEnd { longPressEffect?.handleAnimationComplete() }
+                                doOnCancel { longPressEffect?.handleAnimationCancel() }
+                                start()
+                            }
+                    }
+                }
+
+                override fun onReverseAnimator() {
+                    longPressEffectAnimator?.let {
+                        val pausedProgress = it.animatedFraction
+                        longPressEffect?.playReverseHaptics(pausedProgress)
+                        it.reverse()
+                    }
+                }
+
+                override fun onCancelAnimator() {
+                    resetLongPressEffectProperties()
+                    longPressEffectAnimator?.cancel()
+                }
+            }
+    }
+
+    private fun init(click: OnClickListener?, longClick: OnLongClickListener?) {
         setOnClickListener(click)
         onLongClickListener = longClick
     }
@@ -438,16 +516,18 @@
 
     override fun setClickable(clickable: Boolean) {
         super.setClickable(clickable)
-        if (!Flags.qsTileFocusState()){
-            background = if (clickable && showRippleEffect) {
-                qsTileBackground.also {
-                    // In case that the colorBackgroundDrawable was used as the background, make sure
-                    // it has the correct callback instead of null
-                    backgroundDrawable.callback = it
+        if (!Flags.qsTileFocusState()) {
+            background =
+                if (clickable && showRippleEffect) {
+                    qsTileBackground.also {
+                        // In case that the colorBackgroundDrawable was used as the background, make
+                        // sure
+                        // it has the correct callback instead of null
+                        backgroundDrawable.callback = it
+                    }
+                } else {
+                    backgroundDrawable
                 }
-            } else {
-                backgroundDrawable
-            }
         }
     }
 
@@ -482,8 +562,10 @@
         if (!TextUtils.isEmpty(accessibilityClass)) {
             event.className = accessibilityClass
         }
-        if (event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION &&
-                stateDescriptionDeltas != null) {
+        if (
+            event.contentChangeTypes == AccessibilityEvent.CONTENT_CHANGE_TYPE_STATE_DESCRIPTION &&
+                stateDescriptionDeltas != null
+        ) {
             event.text.add(stateDescriptionDeltas)
             stateDescriptionDeltas = null
         }
@@ -493,36 +575,39 @@
         super.onInitializeAccessibilityNodeInfo(info)
         // Clear selected state so it is not announce by talkback.
         info.isSelected = false
-        info.text = if (TextUtils.isEmpty(secondaryLabel.text)) {
-            "${label.text}"
-        } else {
-            "${label.text}, ${secondaryLabel.text}"
-        }
+        info.text =
+            if (TextUtils.isEmpty(secondaryLabel.text)) {
+                "${label.text}"
+            } else {
+                "${label.text}, ${secondaryLabel.text}"
+            }
         if (lastDisabledByPolicy) {
             info.addAction(
-                    AccessibilityNodeInfo.AccessibilityAction(
-                            AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id,
-                            resources.getString(
-                                R.string.accessibility_tile_disabled_by_policy_action_description
-                            )
+                AccessibilityNodeInfo.AccessibilityAction(
+                    AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id,
+                    resources.getString(
+                        R.string.accessibility_tile_disabled_by_policy_action_description
                     )
+                )
             )
         }
         if (!TextUtils.isEmpty(accessibilityClass)) {
-            info.className = if (lastDisabledByPolicy) {
-                Button::class.java.name
-            } else {
-                accessibilityClass
-            }
+            info.className =
+                if (lastDisabledByPolicy) {
+                    Button::class.java.name
+                } else {
+                    accessibilityClass
+                }
             if (Switch::class.java.name == accessibilityClass) {
                 info.isChecked = tileState
                 info.isCheckable = true
                 if (isLongClickable) {
                     info.addAction(
-                            AccessibilityNodeInfo.AccessibilityAction(
-                                    AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
-                                    resources.getString(
-                                            R.string.accessibility_long_click_tile)))
+                        AccessibilityNodeInfo.AccessibilityAction(
+                            AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+                            resources.getString(R.string.accessibility_long_click_tile)
+                        )
+                    )
                 }
             }
         }
@@ -541,6 +626,28 @@
         return sb.toString()
     }
 
+    @SuppressLint("ClickableViewAccessibility")
+    override fun onTouchEvent(event: MotionEvent?): Boolean {
+        // let the View run the onTouch logic for click and long-click detection
+        val result = super.onTouchEvent(event)
+        if (longPressEffect != null) {
+            when (event?.actionMasked) {
+                MotionEvent.ACTION_DOWN -> {
+                    longPressEffect.handleActionDown()
+                    if (isLongClickable) {
+                        postDelayed(
+                            { longPressEffect.handleTimeoutComplete() },
+                            ViewConfiguration.getTapTimeout().toLong(),
+                        )
+                    }
+                }
+                MotionEvent.ACTION_UP -> longPressEffect.handleActionUp()
+                MotionEvent.ACTION_CANCEL -> longPressEffect.handleActionCancel()
+            }
+        }
+        return result
+    }
+
     // HANDLE STATE CHANGES RELATED METHODS
 
     protected open fun handleStateChanged(state: QSTile.State) {
@@ -565,8 +672,11 @@
         if (!TextUtils.isEmpty(state.stateDescription)) {
             stateDescription.append(", ")
             stateDescription.append(state.stateDescription)
-            if (lastState != INVALID && state.state == lastState &&
-                    state.stateDescription != lastStateDescription) {
+            if (
+                lastState != INVALID &&
+                    state.state == lastState &&
+                    state.stateDescription != lastStateDescription
+            ) {
                 stateDescriptionDeltas = state.stateDescription
             }
         }
@@ -574,11 +684,12 @@
         setStateDescription(stateDescription.toString())
         lastStateDescription = state.stateDescription
 
-        accessibilityClass = if (state.state == Tile.STATE_UNAVAILABLE) {
-            null
-        } else {
-            state.expandedAccessibilityClassName
-        }
+        accessibilityClass =
+            if (state.state == Tile.STATE_UNAVAILABLE) {
+                null
+            } else {
+                state.expandedAccessibilityClassName
+            }
 
         if (state is AdapterState) {
             val newState = state.value
@@ -593,49 +704,51 @@
         }
         if (!Objects.equals(secondaryLabel.text, state.secondaryLabel)) {
             secondaryLabel.text = state.secondaryLabel
-            secondaryLabel.visibility = if (TextUtils.isEmpty(state.secondaryLabel)) {
-                GONE
-            } else {
-                VISIBLE
-            }
+            secondaryLabel.visibility =
+                if (TextUtils.isEmpty(state.secondaryLabel)) {
+                    GONE
+                } else {
+                    VISIBLE
+                }
         }
 
         // Colors
         if (state.state != lastState || state.disabledByPolicy != lastDisabledByPolicy) {
             singleAnimator.cancel()
             mQsLogger?.logTileBackgroundColorUpdateIfInternetTile(
-                    state.spec,
-                    state.state,
-                    state.disabledByPolicy,
-                    getBackgroundColorForState(state.state, state.disabledByPolicy))
+                state.spec,
+                state.state,
+                state.disabledByPolicy,
+                getBackgroundColorForState(state.state, state.disabledByPolicy)
+            )
             if (allowAnimations) {
                 singleAnimator.setValues(
-                        colorValuesHolder(
-                                BACKGROUND_NAME,
-                                backgroundColor,
-                                getBackgroundColorForState(state.state, state.disabledByPolicy)
-                        ),
-                        colorValuesHolder(
-                                LABEL_NAME,
-                                label.currentTextColor,
-                                getLabelColorForState(state.state, state.disabledByPolicy)
-                        ),
-                        colorValuesHolder(
-                                SECONDARY_LABEL_NAME,
-                                secondaryLabel.currentTextColor,
-                                getSecondaryLabelColorForState(state.state, state.disabledByPolicy)
-                        ),
-                        colorValuesHolder(
-                                CHEVRON_NAME,
-                                chevronView.imageTintList?.defaultColor ?: 0,
-                                getChevronColorForState(state.state, state.disabledByPolicy)
-                        ),
-                        colorValuesHolder(
-                                OVERLAY_NAME,
-                                backgroundOverlayColor,
-                                getOverlayColorForState(state.state)
-                        )
+                    colorValuesHolder(
+                        BACKGROUND_NAME,
+                        backgroundColor,
+                        getBackgroundColorForState(state.state, state.disabledByPolicy)
+                    ),
+                    colorValuesHolder(
+                        LABEL_NAME,
+                        label.currentTextColor,
+                        getLabelColorForState(state.state, state.disabledByPolicy)
+                    ),
+                    colorValuesHolder(
+                        SECONDARY_LABEL_NAME,
+                        secondaryLabel.currentTextColor,
+                        getSecondaryLabelColorForState(state.state, state.disabledByPolicy)
+                    ),
+                    colorValuesHolder(
+                        CHEVRON_NAME,
+                        chevronView.imageTintList?.defaultColor ?: 0,
+                        getChevronColorForState(state.state, state.disabledByPolicy)
+                    ),
+                    colorValuesHolder(
+                        OVERLAY_NAME,
+                        backgroundOverlayColor,
+                        getOverlayColorForState(state.state)
                     )
+                )
                 singleAnimator.start()
             } else {
                 setAllColors(
@@ -658,25 +771,16 @@
         lastIconTint = icon.getColor(state)
 
         // Long-press effects
-        if (state.handlesLongClick &&
-            longPressEffect?.initializeEffect(longPressEffectDuration) == true) {
-            // bind the long-press effect and set it as the touch listener
-            if (!isLongPressEffectBound) {
-                longPressEffectHandle =
-                    QSLongPressEffectViewBinder.bind(
-                        this,
-                        longPressEffect,
-                        state.spec,
-                    )
-            }
+        if (
+            state.handlesLongClick &&
+                longPressEffect?.initializeEffect(longPressEffectDuration) == true
+        ) {
             showRippleEffect = false
             initializeLongPressProperties(measuredHeight, measuredWidth)
         } else {
             // Long-press effects might have been enabled before but the new state does not
             // handle a long-press. In this case, we go back to the behaviour of a regular tile
             // and clean-up the resources
-            setOnTouchListener(null)
-            unbindLongPressEffect()
             showRippleEffect = isClickable
             initialLongPressProperties = null
             finalLongPressProperties = null
@@ -791,7 +895,7 @@
     }
 
     private fun getChevronColorForState(state: Int, disabledByPolicy: Boolean = false): Int =
-            getSecondaryLabelColorForState(state, disabledByPolicy)
+        getSecondaryLabelColorForState(state, disabledByPolicy)
 
     private fun getOverlayColorForState(state: Int): Int {
         return when (state) {
@@ -828,16 +932,18 @@
         // Dimensions change
         val newHeight =
             interpolateFloat(
-                effectProgress,
-                initialLongPressProperties?.height ?: 0f,
-                finalLongPressProperties?.height ?: 0f,
-            ).toInt()
+                    effectProgress,
+                    initialLongPressProperties?.height ?: 0f,
+                    finalLongPressProperties?.height ?: 0f,
+                )
+                .toInt()
         val newWidth =
             interpolateFloat(
-                effectProgress,
-                initialLongPressProperties?.width ?: 0f,
-                finalLongPressProperties?.width ?: 0f,
-            ).toInt()
+                    effectProgress,
+                    initialLongPressProperties?.width ?: 0f,
+                    finalLongPressProperties?.width ?: 0f,
+                )
+                .toInt()
 
         val startingHeight = initialLongPressProperties?.height?.toInt() ?: 0
         val startingWidth = initialLongPressProperties?.width?.toInt() ?: 0
@@ -898,11 +1004,6 @@
         )
     }
 
-    private fun unbindLongPressEffect() {
-        longPressEffectHandle?.dispose()
-        longPressEffectHandle = null
-    }
-
     private fun interpolateFloat(fraction: Float, start: Float, end: Float): Float =
         start + fraction * (end - start)
 
@@ -952,6 +1053,7 @@
                 getOverlayColorForState(Tile.STATE_ACTIVE),
                 Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive),
             )
+        prepareForLaunch()
     }
 
     private fun changeCornerRadius(radius: Float) {
@@ -964,12 +1066,13 @@
     }
 
     @VisibleForTesting
-    internal fun getCurrentColors(): List<Int> = listOf(
+    internal fun getCurrentColors(): List<Int> =
+        listOf(
             backgroundColor,
             label.currentTextColor,
             secondaryLabel.currentTextColor,
             chevronView.imageTintList?.defaultColor ?: 0
-    )
+        )
 
     inner class StateChangeRunnable(private val state: QSTile.State) : Runnable {
         override fun run() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 169cdc1..8a72e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -60,7 +60,7 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel;
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.DialogKt;
@@ -193,14 +193,13 @@
     // case where multiple devices were active :-/.
     private boolean willPopDialog() {
         List<CastDevice> activeDevices = getActiveDevices();
-        return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
+        return activeDevices.isEmpty() || (activeDevices.get(0).getTag() instanceof RouteInfo);
     }
 
     private List<CastDevice> getActiveDevices() {
         ArrayList<CastDevice> activeDevices = new ArrayList<>();
         for (CastDevice device : mController.getCastDevices()) {
-            if (device.state == CastDevice.STATE_CONNECTED
-                    || device.state == CastDevice.STATE_CONNECTING) {
+            if (device.isCasting()) {
                 activeDevices.add(device);
             }
         }
@@ -276,7 +275,7 @@
         // We always choose the first device that's in the CONNECTED state in the case where
         // multiple devices are CONNECTED at the same time.
         for (CastDevice device : devices) {
-            if (device.state == CastDevice.STATE_CONNECTED) {
+            if (device.getState() == CastDevice.CastState.Connected) {
                 state.value = true;
                 state.secondaryLabel = getDeviceName(device);
                 state.stateDescription = state.stateDescription + ","
@@ -284,7 +283,7 @@
                         R.string.accessibility_cast_name, state.label);
                 connecting = false;
                 break;
-            } else if (device.state == CastDevice.STATE_CONNECTING) {
+            } else if (device.getState() == CastDevice.CastState.Connecting) {
                 connecting = true;
             }
         }
@@ -315,7 +314,7 @@
     }
 
     private String getDeviceName(CastDevice device) {
-        return device.name != null ? device.name
+        return device.getName() != null ? device.getName()
                 : mContext.getString(R.string.quick_settings_cast_device_default_name);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index 183c1a4..b96e83d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -19,34 +19,53 @@
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.UserManager;
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Flags;
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
 import com.android.systemui.animation.Expandable;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.BluetoothController;
 
 import javax.inject.Inject;
 
 /** Quick settings tile: Hearing Devices **/
-public class HearingDevicesTile extends QSTileImpl<State> {
-
+public class HearingDevicesTile extends QSTileImpl<BooleanState> {
+    //TODO(b/338520598): Transform the current implementation into new QS architecture
+    // and use Kotlin except Tile class.
     public static final String TILE_SPEC = "hearing_devices";
 
     private final HearingDevicesDialogManager mDialogManager;
+    private final HearingDevicesChecker mDevicesChecker;
+    private final BluetoothController mBluetoothController;
+
+    private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
+        @Override
+        public void onBluetoothStateChange(boolean enabled) {
+            refreshState();
+        }
+
+        @Override
+        public void onBluetoothDevicesChanged() {
+            refreshState();
+        }
+    };
 
     @Inject
     public HearingDevicesTile(
@@ -59,16 +78,20 @@
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
-            HearingDevicesDialogManager hearingDevicesDialogManager
-    ) {
+            HearingDevicesDialogManager hearingDevicesDialogManager,
+            HearingDevicesChecker hearingDevicesChecker,
+            BluetoothController bluetoothController) {
         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
         mDialogManager = hearingDevicesDialogManager;
+        mDevicesChecker = hearingDevicesChecker;
+        mBluetoothController = bluetoothController;
+        mBluetoothController.observe(getLifecycle(), mCallback);
     }
 
     @Override
-    public State newTileState() {
-        return new State();
+    public BooleanState newTileState() {
+        return new BooleanState();
     }
 
     @Override
@@ -77,9 +100,28 @@
     }
 
     @Override
-    protected void handleUpdateState(State state, Object arg) {
+    protected void handleUpdateState(BooleanState state, Object arg) {
+        checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH);
+
         state.label = mContext.getString(R.string.quick_settings_hearing_devices_label);
         state.icon = ResourceIcon.get(R.drawable.qs_hearing_devices_icon);
+        state.forceExpandIcon = true;
+
+        boolean isBonded = mDevicesChecker.isAnyPairedHearingDevice();
+        boolean isActive = mDevicesChecker.isAnyActiveHearingDevice();
+
+        if (isActive) {
+            state.state = Tile.STATE_ACTIVE;
+            state.secondaryLabel = mContext.getString(
+                    R.string.quick_settings_hearing_devices_connected);
+        } else if (isBonded) {
+            state.state = Tile.STATE_INACTIVE;
+            state.secondaryLabel = mContext.getString(
+                    R.string.quick_settings_hearing_devices_disconnected);
+        } else {
+            state.state = Tile.STATE_INACTIVE;
+            state.secondaryLabel = "";
+        }
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index e680102..70f3b84 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -52,7 +52,6 @@
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.traceur.TraceUtils.PresetTraceType
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -131,15 +130,11 @@
         }
     }
 
-    private fun startIssueRecordingService(screenRecord: Boolean, traceType: PresetTraceType) =
+    private fun startIssueRecordingService() =
         PendingIntent.getForegroundService(
                 userContextProvider.userContext,
                 RecordingService.REQUEST_CODE,
-                IssueRecordingService.getStartIntent(
-                    userContextProvider.userContext,
-                    screenRecord,
-                    traceType
-                ),
+                IssueRecordingService.getStartIntent(userContextProvider.userContext),
                 PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
             )
             .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
@@ -157,7 +152,7 @@
         val dialog: AlertDialog =
             delegateFactory
                 .create {
-                    startIssueRecordingService(it.screenRecord, it.traceType)
+                    startIssueRecordingService()
                     dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
                     panelInteractor.collapsePanels()
                 }
@@ -169,8 +164,7 @@
                 if (expandable != null && !keyguardStateController.isShowing) {
                     expandable
                         .dialogTransitionController(DialogCuj(CUJ_SHADE_DIALOG_OPEN, TILE_SPEC))
-                        ?.let { dialogTransitionAnimator.show(dialog, it) }
-                        ?: dialog.show()
+                        ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show()
                 } else {
                     dialog.show()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
index 9752fea..9a776f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
@@ -19,6 +19,7 @@
 import android.os.UserHandle
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
 
 /**
  * Provides data and availability for the tile. In most cases it would delegate data retrieval to
@@ -26,7 +27,7 @@
  * these methods because there is no background thread guarantee. Use [Flow.flowOn] (typically
  * with @Background [CoroutineDispatcher]) instead to move the calculations to another thread.
  */
-interface QSTileDataInteractor<DATA_TYPE> {
+interface QSTileDataInteractor<DATA_TYPE> : QSTileAvailabilityInteractor {
 
     /**
      * Returns a data flow scoped to the user. This means the subscription will live when the tile
@@ -36,7 +37,9 @@
      * as possible.
      */
     fun tileData(user: UserHandle, triggers: Flow<DataUpdateTrigger>): Flow<DATA_TYPE>
+}
 
+interface QSTileAvailabilityInteractor {
     /**
      * Returns tile availability - whether this device currently supports this tile.
      *
@@ -44,4 +47,12 @@
      * as possible.
      */
     fun availability(user: UserHandle): Flow<Boolean>
+
+    companion object {
+        val AlwaysAvailableInteractor = object : QSTileAvailabilityInteractor {
+            override fun availability(user: UserHandle): Flow<Boolean> {
+                return flowOf(true)
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
index b325b4d..b766ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.qs.external.CustomTileStatePersisterImpl
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
@@ -51,6 +52,8 @@
      */
     @Multibinds fun tileViewModelMap(): Map<String, QSTileViewModel>
 
+    @Multibinds fun tileAvailabilityInteractors(): Map<String, QSTileAvailabilityInteractor>
+
     @Binds fun bindQSTileConfigProvider(impl: QSTileConfigProviderImpl): QSTileConfigProvider
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index 79720c1..5637115 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.screenrecord.RecordingController
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
@@ -47,6 +48,7 @@
     @Application private val context: Context,
     @Main private val mainContext: CoroutineContext,
     @Background private val backgroundContext: CoroutineContext,
+    private val screenRecordRepository: ScreenRecordRepository,
     private val recordingController: RecordingController,
     private val keyguardInteractor: KeyguardInteractor,
     private val keyguardDismissUtil: KeyguardDismissUtil,
@@ -65,8 +67,7 @@
                             Log.d(TAG, "Cancelling countdown")
                             withContext(backgroundContext) { recordingController.cancelCountdown() }
                         }
-                        is ScreenRecordModel.Recording ->
-                            withContext(backgroundContext) { recordingController.stopRecording() }
+                        is ScreenRecordModel.Recording -> screenRecordRepository.stopRecording()
                         is ScreenRecordModel.DoingNothing ->
                             withContext(mainContext) {
                                 showPrompt(action.expandable, user.identifier)
@@ -122,8 +123,7 @@
                             controller,
                             animateBackgroundBoundsChange = true,
                         )
-                    }
-                        ?: dialog.show()
+                    } ?: dialog.show()
                 } else {
                     dialog.show()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index c7326b08..bb36fd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.qs.QSContainerImpl
 import com.android.systemui.qs.QSImpl
 import com.android.systemui.qs.dagger.QSSceneComponent
+import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel.state
 import com.android.systemui.res.R
 import com.android.systemui.settings.brightness.MirrorController
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -267,6 +268,7 @@
 
     override val qqsHeight: Int
         get() = qsImpl.value?.qqsHeight ?: 0
+
     override val qsHeight: Int
         get() = qsImpl.value?.qsHeight ?: 0
 
@@ -375,8 +377,10 @@
             qs.view.setPadding(0, 0, 0, 0)
             qs.setContainerController(this@QSSceneAdapterImpl)
             qs.applyState(state.value)
+            applyLatestExpansionAndSquishiness()
         }
     }
+
     override fun setState(state: QSSceneAdapter.State) {
         this.state.value = state
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
index 6cf2e52..79cdfec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.qs.ui.viewmodel
 
 import androidx.lifecycle.LifecycleOwner
@@ -28,12 +26,12 @@
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.qs.FooterActionsController
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.scene.domain.interactor.SceneBackInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
@@ -41,7 +39,6 @@
 import java.util.concurrent.atomic.AtomicBoolean
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
@@ -55,7 +52,6 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    deviceEntryInteractor: DeviceEntryInteractor,
     val brightnessMirrorViewModel: BrightnessMirrorViewModel,
     val shadeHeaderViewModel: ShadeHeaderViewModel,
     val qsSceneAdapter: QSSceneAdapter,
@@ -77,25 +73,15 @@
 
     val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
         combine(
-                deviceEntryInteractor.isUnlocked,
-                deviceEntryInteractor.canSwipeToEnter,
                 qsSceneAdapter.isCustomizerShowing,
                 backScene,
-            ) { isUnlocked, canSwipeToDismiss, isCustomizerShowing, backScene ->
-                destinationScenes(
-                    isUnlocked,
-                    canSwipeToDismiss,
-                    isCustomizerShowing,
-                    backScene,
-                )
-            }
+                transform = ::destinationScenes,
+            )
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.WhileSubscribed(),
                 initialValue =
                     destinationScenes(
-                        isUnlocked = deviceEntryInteractor.isUnlocked.value,
-                        canSwipeToDismiss = deviceEntryInteractor.canSwipeToEnter.value,
                         isCustomizing = qsSceneAdapter.isCustomizerShowing.value,
                         backScene = backScene.value,
                     ),
@@ -104,18 +90,9 @@
     val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation
 
     private fun destinationScenes(
-        isUnlocked: Boolean,
-        canSwipeToDismiss: Boolean?,
         isCustomizing: Boolean,
         backScene: SceneKey?,
     ): Map<UserAction, UserActionResult> {
-        val upBottomEdge =
-            when {
-                canSwipeToDismiss == true -> Scenes.Lockscreen
-                isUnlocked -> Scenes.Gone
-                else -> Scenes.Lockscreen
-            }
-
         return buildMap {
             if (isCustomizing) {
                 // TODO(b/332749288) Empty map so there are no back handlers and back can close
@@ -127,11 +104,8 @@
                 put(Back, UserActionResult(backScene ?: Scenes.Shade))
                 put(Swipe(SwipeDirection.Up), UserActionResult(backScene ?: Scenes.Shade))
                 put(
-                    Swipe(
-                        fromSource = Edge.Bottom,
-                        direction = SwipeDirection.Up,
-                    ),
-                    UserActionResult(upBottomEdge),
+                    Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
+                    UserActionResult(SceneFamilies.Home),
                 )
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
index c1a5646..bd748d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
@@ -17,30 +17,26 @@
 package com.android.systemui.qs.ui.viewmodel
 
 import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.asStateFlow
 
 /** Models UI state and handles user input for the Quick Settings Shade scene. */
 @SysUISingleton
 class QuickSettingsShadeSceneViewModel
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
     val overlayShadeViewModel: OverlayShadeViewModel,
     val brightnessSliderViewModel: BrightnessSliderViewModel,
     val tileGridViewModel: TileGridViewModel,
@@ -48,18 +44,11 @@
     val qsSceneAdapter: QSSceneAdapter,
 ) {
     val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
-        overlayShadeViewModel.backgroundScene
-            .map(::destinationScenes)
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = destinationScenes(overlayShadeViewModel.backgroundScene.value),
+        MutableStateFlow(
+                mapOf(
+                    Swipe.Up to SceneFamilies.Home,
+                    Back to SceneFamilies.Home,
+                )
             )
-
-    private fun destinationScenes(backgroundScene: SceneKey): Map<UserAction, UserActionResult> {
-        return mapOf(
-            Swipe.Up to backgroundScene,
-            Back to backgroundScene,
-        )
-    }
+            .asStateFlow()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 0327ec7..23faf7d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -73,7 +73,6 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.compose.animation.scene.SceneKey;
 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
@@ -99,12 +98,10 @@
 import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
 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.scene.shared.model.SceneFamilies;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.shade.domain.interactor.ShadeInteractor;
-import com.android.systemui.shade.shared.model.ShadeMode;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -158,7 +155,6 @@
     private final ScreenPinningRequest mScreenPinningRequest;
     private final NotificationShadeWindowController mStatusBarWinController;
     private final Provider<SceneInteractor> mSceneInteractor;
-    private final Provider<ShadeInteractor> mShadeInteractor;
 
     private final Runnable mConnectionRunnable = () ->
             internalConnectToCurrentUser("runnable: startConnectionToCurrentUser");
@@ -247,7 +243,7 @@
                             // Gesture was too short to be picked up by scene container touch
                             // handling; programmatically start the transition to shade scene.
                             mSceneInteractor.get().changeScene(
-                                    getShadeSceneKey(),
+                                    SceneFamilies.NotifShade,
                                     "short launcher swipe"
                             );
                         }
@@ -267,7 +263,7 @@
                                 "trackpad swipe");
                     } else if (action == ACTION_UP) {
                         mSceneInteractor.get().changeScene(
-                                getShadeSceneKey(),
+                                SceneFamilies.NotifShade,
                                 "short trackpad swipe"
                         );
                     }
@@ -632,7 +628,6 @@
             NotificationShadeWindowController statusBarWinController,
             SysUiState sysUiState,
             Provider<SceneInteractor> sceneInteractor,
-            Provider<ShadeInteractor> shadeInteractor,
             UserTracker userTracker,
             WakefulnessLifecycle wakefulnessLifecycle,
             UiEventLogger uiEventLogger,
@@ -659,7 +654,6 @@
         mScreenPinningRequest = screenPinningRequest;
         mStatusBarWinController = statusBarWinController;
         mSceneInteractor = sceneInteractor;
-        mShadeInteractor = shadeInteractor;
         mUserTracker = userTracker;
         mConnectionBackoffAttempts = 0;
         mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
@@ -925,12 +919,6 @@
         }
     }
 
-    private SceneKey getShadeSceneKey() {
-        return mShadeInteractor.get().getShadeMode().getValue() == ShadeMode.dual()
-                ? Scenes.NotificationsShade
-                : Scenes.Shade;
-    }
-
     private void notifyHomeRotationEnabled(boolean enabled) {
         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
             mConnectionCallbacks.get(i).onHomeRotationEnabled(enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 5ede64a..4a4c73b 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -35,8 +35,6 @@
 import com.android.systemui.screenrecord.RecordingServiceStrings
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
-import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE
-import com.android.traceur.TraceUtils.PresetTraceType
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
@@ -76,15 +74,10 @@
         when (intent?.action) {
             ACTION_START -> {
                 bgExecutor.execute {
-                    traceurMessageSender.startTracing(
-                        intent.getSerializableExtra(
-                            INTENT_EXTRA_TRACE_TYPE,
-                            PresetTraceType::class.java
-                        )
-                    )
+                    traceurMessageSender.startTracing(issueRecordingState.traceType)
                 }
                 issueRecordingState.isRecording = true
-                if (!intent.getBooleanExtra(EXTRA_SCREEN_RECORD, false)) {
+                if (!issueRecordingState.recordScreen) {
                     // If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action
                     // will circumvent the RecordingService's screen recording start code.
                     return super.onStartCommand(Intent(ACTION_SHOW_START_NOTIF), flags, startId)
@@ -107,7 +100,7 @@
                     )
 
                     val screenRecording = intent.getParcelableExtra(EXTRA_PATH, Uri::class.java)
-                    if (issueRecordingState.takeBugReport) {
+                    if (issueRecordingState.takeBugreport) {
                         iActivityManager.requestBugReportWithExtraAttachment(screenRecording)
                     } else {
                         traceurMessageSender.shareTraces(applicationContext, screenRecording)
@@ -130,7 +123,6 @@
     companion object {
         private const val TAG = "IssueRecordingService"
         private const val CHANNEL_ID = "issue_record"
-        private const val EXTRA_SCREEN_RECORD = "extra_screenRecord"
 
         /**
          * Get an intent to stop the issue recording service.
@@ -148,35 +140,36 @@
          *
          * @param context Context from the requesting activity
          */
-        fun getStartIntent(
-            context: Context,
-            screenRecord: Boolean,
-            traceType: PresetTraceType,
-        ): Intent =
-            Intent(context, IssueRecordingService::class.java)
-                .setAction(ACTION_START)
-                .putExtra(EXTRA_SCREEN_RECORD, screenRecord)
-                .putExtra(INTENT_EXTRA_TRACE_TYPE, traceType)
+        fun getStartIntent(context: Context): Intent =
+            Intent(context, IssueRecordingService::class.java).setAction(ACTION_START)
     }
 }
 
 private class IrsStrings(private val res: Resources) : RecordingServiceStrings(res) {
     override val title
         get() = res.getString(R.string.issuerecord_title)
+
     override val notificationChannelDescription
         get() = res.getString(R.string.issuerecord_channel_description)
+
     override val startErrorResId
         get() = R.string.issuerecord_start_error
+
     override val startError
         get() = res.getString(R.string.issuerecord_start_error)
+
     override val saveErrorResId
         get() = R.string.issuerecord_save_error
+
     override val saveError
         get() = res.getString(R.string.issuerecord_save_error)
+
     override val ongoingRecording
         get() = res.getString(R.string.issuerecord_ongoing_screen_only)
+
     override val backgroundProcessingLabel
         get() = res.getString(R.string.issuerecord_background_processing_label)
+
     override val saveTitle
         get() = res.getString(R.string.issuerecord_save_title)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
index 12ed06d..4ea3345 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
@@ -16,23 +16,62 @@
 
 package com.android.systemui.recordissue
 
+import android.content.Context
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.tiles.RecordIssueTile
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.settings.UserTracker
+import com.android.traceur.TraceUtils.PresetTraceType
 import java.util.concurrent.CopyOnWriteArrayList
 import javax.inject.Inject
 
 @SysUISingleton
-class IssueRecordingState @Inject constructor() {
+class IssueRecordingState
+@Inject
+constructor(
+    userTracker: UserTracker,
+    userFileManager: UserFileManager,
+) {
+
+    private val prefs =
+        userFileManager.getSharedPreferences(
+            RecordIssueTile.TILE_SPEC,
+            Context.MODE_PRIVATE,
+            userTracker.userId
+        )
+
+    var takeBugreport
+        get() = prefs.getBoolean(KEY_TAKE_BUG_REPORT, false)
+        set(value) = prefs.edit().putBoolean(KEY_TAKE_BUG_REPORT, value).apply()
+
+    var recordScreen
+        get() = prefs.getBoolean(KEY_RECORD_SCREEN, false)
+        set(value) = prefs.edit().putBoolean(KEY_RECORD_SCREEN, value).apply()
+
+    var hasUserApprovedScreenRecording
+        get() = prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)
+        private set(value) = prefs.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, value).apply()
+
+    var issueTypeRes
+        get() = prefs.getInt(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET)
+        set(value) = prefs.edit().putInt(KEY_ISSUE_TYPE_RES, value).apply()
+
+    val traceType: PresetTraceType
+        get() = ALL_ISSUE_TYPES[issueTypeRes] ?: PresetTraceType.UNSET
 
     private val listeners = CopyOnWriteArrayList<Runnable>()
 
-    var takeBugReport: Boolean = false
-
     var isRecording = false
         set(value) {
             field = value
             listeners.forEach(Runnable::run)
         }
 
+    fun markUserApprovalForScreenRecording() {
+        hasUserApprovedScreenRecording = true
+    }
+
     fun addListener(listener: Runnable) {
         listeners.add(listener)
     }
@@ -40,4 +79,20 @@
     fun removeListener(listener: Runnable) {
         listeners.remove(listener)
     }
+
+    companion object {
+        private const val KEY_TAKE_BUG_REPORT = "key_takeBugReport"
+        private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
+        private const val KEY_RECORD_SCREEN = "key_recordScreen"
+        const val KEY_ISSUE_TYPE_RES = "key_issueTypeRes"
+        const val ISSUE_TYPE_NOT_SET = -1
+
+        val ALL_ISSUE_TYPES: Map<Int, PresetTraceType> =
+            hashMapOf(
+                Pair(R.string.performance, PresetTraceType.PERFORMANCE),
+                Pair(R.string.user_interface, PresetTraceType.UI),
+                Pair(R.string.battery, PresetTraceType.BATTERY),
+                Pair(R.string.thermal, PresetTraceType.THERMAL)
+            )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 84a063a..bbf4e51 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.recordissue
 
 import android.annotation.SuppressLint
-import android.app.AlertDialog
+import android.app.AlertDialog.BUTTON_POSITIVE
 import android.content.Context
 import android.content.Intent
 import android.content.res.ColorStateList
@@ -41,18 +41,16 @@
 import com.android.systemui.mediaprojection.SessionCreationSource
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate
-import com.android.systemui.qs.tiles.RecordIssueTile
+import com.android.systemui.recordissue.IssueRecordingState.Companion.ALL_ISSUE_TYPES
+import com.android.systemui.recordissue.IssueRecordingState.Companion.ISSUE_TYPE_NOT_SET
+import com.android.systemui.recordissue.IssueRecordingState.Companion.KEY_ISSUE_TYPE_RES
 import com.android.systemui.res.R
-import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE
-import com.android.traceur.TraceUtils.PresetTraceType
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import java.util.concurrent.Executor
-import java.util.function.Consumer
 
 class RecordIssueDialogDelegate
 @AssistedInject
@@ -64,31 +62,20 @@
     @Main private val mainExecutor: Executor,
     private val devicePolicyResolver: dagger.Lazy<ScreenCaptureDevicePolicyResolver>,
     private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
-    private val userFileManager: UserFileManager,
     private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate,
-    private val issueRecordingState: IssueRecordingState,
+    private val state: IssueRecordingState,
     private val traceurMessageSender: TraceurMessageSender,
-    @Assisted private val onStarted: Consumer<IssueRecordingConfig>,
+    @Assisted private val onStarted: Runnable,
 ) : SystemUIDialog.Delegate {
 
-    private val issueTypeOptions: Map<Int, PresetTraceType> =
-        hashMapOf(
-            Pair(R.string.performance, PresetTraceType.PERFORMANCE),
-            Pair(R.string.user_interface, PresetTraceType.UI),
-            Pair(R.string.battery, PresetTraceType.BATTERY),
-            Pair(R.string.thermal, PresetTraceType.THERMAL)
-        )
-    private var selectedIssueType: PresetTraceType? = null
-
     /** To inject dependencies and allow for easier testing */
     @AssistedFactory
     interface Factory {
         /** Create a dialog object */
-        fun create(onStarted: Consumer<IssueRecordingConfig>): RecordIssueDialogDelegate
+        fun create(onStarted: Runnable): RecordIssueDialogDelegate
     }
 
     @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var screenRecordSwitch: Switch
-    @SuppressLint("UseSwitchCompatOrMaterialCode") private lateinit var bugReportSwitch: Switch
     private lateinit var issueTypeButton: Button
 
     @MainThread
@@ -97,21 +84,8 @@
             setView(LayoutInflater.from(context).inflate(R.layout.record_issue_dialog, null))
             setTitle(context.getString(R.string.qs_record_issue_label))
             setIcon(R.drawable.qs_record_issue_icon_off)
-            setNegativeButton(R.string.cancel) { _, _ -> dismiss() }
-            setPositiveButton(
-                R.string.qs_record_issue_start,
-                { _, _ ->
-                    issueRecordingState.takeBugReport = bugReportSwitch.isChecked
-                    onStarted.accept(
-                        IssueRecordingConfig(
-                            screenRecordSwitch.isChecked,
-                            selectedIssueType ?: PresetTraceType.UNSET
-                        )
-                    )
-                    dismiss()
-                },
-                false
-            )
+            setNegativeButton(R.string.cancel) { _, _ -> }
+            setPositiveButton(R.string.qs_record_issue_start) { _, _ -> onStarted.run() }
         }
         bgExecutor.execute { traceurMessageSender.bindToTraceur(dialog.context) }
     }
@@ -121,22 +95,39 @@
     @MainThread
     override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
         dialog.apply {
-            window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
-            window?.setGravity(Gravity.CENTER)
+            window?.apply {
+                addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+                setGravity(Gravity.CENTER)
+            }
 
-            screenRecordSwitch = requireViewById(R.id.screenrecord_switch)
-            screenRecordSwitch.setOnCheckedChangeListener { _, isEnabled ->
-                if (isEnabled) {
-                    bgExecutor.execute { onScreenRecordSwitchClicked() }
+            screenRecordSwitch =
+                requireViewById<Switch>(R.id.screenrecord_switch).apply {
+                    isChecked = state.recordScreen
+                    setOnCheckedChangeListener { _, isChecked ->
+                        state.recordScreen = isChecked
+                        if (isChecked) {
+                            bgExecutor.execute { onScreenRecordSwitchClicked() }
+                        }
+                    }
                 }
+
+            requireViewById<Switch>(R.id.bugreport_switch).apply {
+                isChecked = state.takeBugreport
+                setOnCheckedChangeListener { _, isChecked -> state.takeBugreport = isChecked }
             }
-            bugReportSwitch = requireViewById(R.id.bugreport_switch)
-            val startButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE)
-            issueTypeButton = requireViewById(R.id.issue_type_button)
-            issueTypeButton.setOnClickListener {
-                onIssueTypeClicked(context) { startButton.isEnabled = true }
-            }
-            startButton.isEnabled = false
+
+            issueTypeButton =
+                requireViewById<Button>(R.id.issue_type_button).apply {
+                    val startButton = dialog.getButton(BUTTON_POSITIVE)
+                    if (state.issueTypeRes != ISSUE_TYPE_NOT_SET) {
+                        setText(state.issueTypeRes)
+                    } else {
+                        startButton.isEnabled = false
+                    }
+                    setOnClickListener {
+                        onIssueTypeClicked(context) { startButton.isEnabled = true }
+                    }
+                }
         }
     }
 
@@ -160,19 +151,14 @@
             SessionCreationSource.SYSTEM_UI_SCREEN_RECORDER
         )
 
-        if (flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)) {
-            val prefs =
-                userFileManager.getSharedPreferences(
-                    RecordIssueTile.TILE_SPEC,
-                    Context.MODE_PRIVATE,
-                    userTracker.userId
-                )
-            if (!prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)) {
-                mainExecutor.execute {
-                    ScreenCapturePermissionDialogDelegate(factory, prefs).createDialog().apply {
-                        setOnCancelListener { screenRecordSwitch.isChecked = false }
-                        show()
-                    }
+        if (
+            flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING) &&
+                !state.hasUserApprovedScreenRecording
+        ) {
+            mainExecutor.execute {
+                ScreenCapturePermissionDialogDelegate(factory, state).createDialog().apply {
+                    setOnCancelListener { screenRecordSwitch.isChecked = false }
+                    show()
                 }
             }
         }
@@ -182,23 +168,21 @@
     private fun onIssueTypeClicked(context: Context, onIssueTypeSelected: Runnable) {
         val popupMenu = PopupMenu(context, issueTypeButton)
 
-        issueTypeOptions.keys.forEach {
+        ALL_ISSUE_TYPES.keys.forEach {
             popupMenu.menu.add(it).apply {
                 setIcon(R.drawable.arrow_pointing_down)
-                if (issueTypeOptions[it] != selectedIssueType) {
+                if (it != state.issueTypeRes) {
                     iconTintList = ColorStateList.valueOf(Color.TRANSPARENT)
                 }
-                intent = Intent().putExtra(INTENT_EXTRA_TRACE_TYPE, issueTypeOptions[it])
+                intent = Intent().putExtra(KEY_ISSUE_TYPE_RES, it)
             }
         }
         popupMenu.apply {
             setOnMenuItemClickListener {
                 issueTypeButton.text = it.title
-                selectedIssueType =
-                    it.intent?.getSerializableExtra(
-                        INTENT_EXTRA_TRACE_TYPE,
-                        PresetTraceType::class.java
-                    )
+                state.issueTypeRes =
+                    it.intent?.getIntExtra(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET)
+                        ?: ISSUE_TYPE_NOT_SET
                 onIssueTypeSelected.run()
                 true
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
index de6d3f6..b029d07 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/ScreenCapturePermissionDialogDelegate.kt
@@ -16,18 +16,15 @@
 
 package com.android.systemui.recordissue
 
-import android.content.SharedPreferences
 import android.os.Bundle
 import android.view.Gravity
 import android.view.WindowManager
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
 
-const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
-
 class ScreenCapturePermissionDialogDelegate(
     private val dialogFactory: SystemUIDialog.Factory,
-    private val sharedPreferences: SharedPreferences,
+    private val state: IssueRecordingState,
 ) : SystemUIDialog.Delegate {
 
     override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
@@ -37,7 +34,7 @@
             setMessage(R.string.screenrecord_permission_dialog_warning_entire_screen)
             setNegativeButton(R.string.slice_permission_deny) { _, _ -> cancel() }
             setPositiveButton(R.string.slice_permission_allow) { _, _ ->
-                sharedPreferences.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, true).apply()
+                state.markUserApprovalForScreenRecording()
                 dismiss()
             }
             window?.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
index 189336c..51744aa 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
@@ -93,7 +93,7 @@
     }
 
     @WorkerThread
-    fun startTracing(traceType: PresetTraceType?) {
+    fun startTracing(traceType: PresetTraceType) {
         val data =
             Bundle().apply { putSerializable(MessageConstants.INTENT_EXTRA_TRACE_TYPE, traceType) }
         notifyTraceur(MessageConstants.START_WHAT, data)
diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
index eb64dd6..0589e6c 100644
--- a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.camera.CameraRotationModule
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileDataInteractor
 import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileUserActionInteractor
@@ -28,6 +29,7 @@
 import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
 import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
 import com.android.systemui.res.R
+import dagger.Binds
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.IntoMap
@@ -35,6 +37,13 @@
 
 @Module(includes = [CameraRotationModule::class])
 interface RotationLockNewModule {
+
+    @Binds
+    @IntoMap
+    @StringKey(ROTATION_TILE_SPEC)
+    fun provideRotationAvailabilityInteractor(
+            impl: RotationLockTileDataInteractor
+    ): QSTileAvailabilityInteractor
     companion object {
         private const val ROTATION_TILE_SPEC = "rotation"
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index 7a9d09a..323ca87 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -18,7 +18,11 @@
 
 import com.android.systemui.CoreStartable
 import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
+import com.android.systemui.scene.domain.SceneDomainModule
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
+import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule
+import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule
 import com.android.systemui.scene.domain.startable.SceneContainerStartable
 import com.android.systemui.scene.domain.startable.ScrimStartable
 import com.android.systemui.scene.shared.model.SceneContainerConfig
@@ -40,6 +44,12 @@
             NotificationsShadeSessionModule::class,
             QuickSettingsSceneModule::class,
             ShadeSceneModule::class,
+            SceneDomainModule::class,
+
+            // List SceneResolver modules for supported SceneFamilies
+            HomeSceneFamilyResolverModule::class,
+            NotifShadeSceneFamilyResolverModule::class,
+            QuickSettingsSceneFamilyResolverModule::class,
         ],
 )
 interface KeyguardlessSceneContainerFrameworkModule {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 7e6dfb8..4691eba 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -19,7 +19,11 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule
 import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
+import com.android.systemui.scene.domain.SceneDomainModule
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
+import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolverModule
+import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolverModule
 import com.android.systemui.scene.domain.startable.SceneContainerStartable
 import com.android.systemui.scene.domain.startable.ScrimStartable
 import com.android.systemui.scene.shared.model.SceneContainerConfig
@@ -46,6 +50,12 @@
             QuickSettingsShadeSceneModule::class,
             NotificationsShadeSceneModule::class,
             NotificationsShadeSessionModule::class,
+            SceneDomainModule::class,
+
+            // List SceneResolver modules for supported SceneFamilies
+            HomeSceneFamilyResolverModule::class,
+            NotifShadeSceneFamilyResolverModule::class,
+            QuickSettingsSceneFamilyResolverModule::class,
         ],
 )
 interface SceneContainerFrameworkModule {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
index b918277..9a7eef8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.scene
 
+import com.android.systemui.scene.domain.SceneDomainModule
+import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
 import dagger.Module
@@ -29,6 +31,10 @@
             EmptySceneModule::class,
             GoneSceneModule::class,
             LockscreenSceneModule::class,
+            SceneDomainModule::class,
+
+            // List SceneResolver modules for supported SceneFamilies
+            HomeSceneFamilyResolverModule::class,
         ],
 )
 object ShadelessSceneContainerFrameworkModule {
@@ -47,11 +53,12 @@
                     Scenes.Bouncer,
                 ),
             initialSceneKey = Scenes.Lockscreen,
-            mapOf(
-                Scenes.Gone to 0,
-                Scenes.Lockscreen to 0,
-                Scenes.Bouncer to 1,
-            )
+            navigationDistances =
+                mapOf(
+                    Scenes.Gone to 0,
+                    Scenes.Lockscreen to 0,
+                    Scenes.Bouncer to 1,
+                )
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
similarity index 71%
copy from packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
copy to packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
index 23dbc26..be792df 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
@@ -14,8 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recordissue
+package com.android.systemui.scene.domain
 
-import com.android.traceur.TraceUtils.PresetTraceType
+import com.android.systemui.scene.domain.resolver.SceneResolverModule
+import dagger.Module
 
-data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
+@Module(
+    includes =
+        [
+            SceneResolverModule::class,
+        ]
+)
+object SceneDomainModule
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index b1700e3..4738dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -23,9 +23,12 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.scene.data.repository.SceneContainerRepository
+import com.android.systemui.scene.domain.resolver.SceneResolver
 import com.android.systemui.scene.shared.logger.SceneLogger
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.util.kotlin.pairwiseBy
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,9 +36,12 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.emitAll
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -52,6 +58,7 @@
     @Application private val applicationScope: CoroutineScope,
     private val repository: SceneContainerRepository,
     private val logger: SceneLogger,
+    private val sceneFamilyResolvers: Lazy<Map<SceneKey, @JvmSuppressWildcards SceneResolver>>,
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
 ) {
 
@@ -96,7 +103,14 @@
      * 2. When transitioning, which scenes are being transitioned between.
      * 3. When transitioning, what the progress of the transition is.
      */
-    val transitionState: StateFlow<ObservableTransitionState> = repository.transitionState
+    val transitionState: StateFlow<ObservableTransitionState> =
+        repository.transitionState
+            .onEach { logger.logSceneTransition(it) }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = repository.transitionState.value,
+            )
 
     /**
      * The key of the scene that the UI is currently transitioning to or `null` if there is no
@@ -152,6 +166,28 @@
             )
 
     /**
+     * The amount of transition into or out of the given [scene].
+     *
+     * The value will be `0` if not in this scene or `1` when fully in the given scene.
+     */
+    fun transitionProgress(scene: SceneKey): Flow<Float> {
+        return transitionState.flatMapLatest { transition ->
+            when (transition) {
+                is ObservableTransitionState.Idle -> {
+                    flowOf(if (transition.currentScene == scene) 1f else 0f)
+                }
+                is ObservableTransitionState.Transition -> {
+                    when {
+                        transition.toScene == scene -> transition.progress
+                        transition.fromScene == scene -> transition.progress.map { 1f - it }
+                        else -> flowOf(0f)
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Returns the keys of all scenes in the container.
      *
      * The scenes will be sorted in z-order such that the last one is the one that should be
@@ -180,10 +216,11 @@
         sceneState: Any? = null,
     ) {
         val currentSceneKey = currentScene.value
+        val resolvedScene = sceneFamilyResolvers.get()[toScene]?.resolvedScene?.value ?: toScene
         if (
             !validateSceneChange(
                 from = currentSceneKey,
-                to = toScene,
+                to = resolvedScene,
                 loggingReason = loggingReason,
             )
         ) {
@@ -192,13 +229,13 @@
 
         logger.logSceneChangeRequested(
             from = currentSceneKey,
-            to = toScene,
+            to = resolvedScene,
             reason = loggingReason,
             isInstant = false,
         )
 
-        onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(toScene, sceneState) }
-        repository.changeScene(toScene, transitionKey)
+        onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(resolvedScene, sceneState) }
+        repository.changeScene(resolvedScene, transitionKey)
     }
 
     /**
@@ -212,10 +249,18 @@
         loggingReason: String,
     ) {
         val currentSceneKey = currentScene.value
+        val resolvedScene =
+            sceneFamilyResolvers.get()[toScene]?.let { familyResolver ->
+                if (familyResolver.includesScene(currentSceneKey)) {
+                    return
+                } else {
+                    familyResolver.resolvedScene.value
+                }
+            } ?: toScene
         if (
             !validateSceneChange(
                 from = currentSceneKey,
-                to = toScene,
+                to = resolvedScene,
                 loggingReason = loggingReason,
             )
         ) {
@@ -224,12 +269,12 @@
 
         logger.logSceneChangeRequested(
             from = currentSceneKey,
-            to = toScene,
+            to = resolvedScene,
             reason = loggingReason,
             isInstant = true,
         )
 
-        repository.snapToScene(toScene)
+        repository.snapToScene(resolvedScene)
     }
 
     /**
@@ -288,6 +333,21 @@
         repository.setTransitionState(transitionState)
     }
 
+    /**
+     * Returns the [concrete scene][Scenes] for [sceneKey] if it is a [scene family][SceneFamilies],
+     * otherwise returns a singleton [Flow] containing [sceneKey].
+     */
+    fun resolveSceneFamily(sceneKey: SceneKey): Flow<SceneKey> = flow {
+        emitAll(resolveSceneFamilyOrNull(sceneKey) ?: flowOf(sceneKey))
+    }
+
+    /**
+     * Returns the [concrete scene][Scenes] for [sceneKey] if it is a [scene family][SceneFamilies],
+     * otherwise returns `null`.
+     */
+    fun resolveSceneFamilyOrNull(sceneKey: SceneKey): StateFlow<SceneKey>? =
+        sceneFamilyResolvers.get()[sceneKey]?.resolvedScene
+
     private fun isVisibleInternal(
         raw: Boolean = repository.isVisible.value,
         isRemoteUserInteractionOngoing: Boolean = repository.isRemoteUserInteractionOngoing.value,
@@ -330,4 +390,12 @@
 
         return from != to
     }
+
+    /** Returns a flow indicating if the currently visible scene can be resolved from [family]. */
+    fun isCurrentSceneInFamily(family: SceneKey): Flow<Boolean> =
+        currentScene.map { currentScene -> isSceneInFamily(currentScene, family) }
+
+    /** Returns `true` if [scene] can be resolved from [family]. */
+    fun isSceneInFamily(scene: SceneKey, family: SceneKey): Boolean =
+        sceneFamilyResolvers.get()[family]?.includesScene(scene) == true
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
new file mode 100644
index 0000000..9e91b66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/HomeSceneFamilyResolver.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.domain.resolver
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Resolver for [SceneFamilies.Home]. The "home" scene family resolves to the scene that is
+ * currently underneath any "overlay" scene, such as shades or bouncer.
+ */
+@SysUISingleton
+class HomeSceneFamilyResolver
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    deviceEntryInteractor: DeviceEntryInteractor,
+) : SceneResolver {
+    override val targetFamily: SceneKey = SceneFamilies.Home
+
+    override val resolvedScene: StateFlow<SceneKey> =
+        combine(
+                deviceEntryInteractor.canSwipeToEnter,
+                deviceEntryInteractor.isDeviceEntered,
+                deviceEntryInteractor.isUnlocked,
+                transform = ::homeScene,
+            )
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue =
+                    homeScene(
+                        deviceEntryInteractor.canSwipeToEnter.value,
+                        deviceEntryInteractor.isDeviceEntered.value,
+                        deviceEntryInteractor.isUnlocked.value,
+                    )
+            )
+
+    override fun includesScene(scene: SceneKey): Boolean = scene in homeScenes
+
+    private fun homeScene(
+        canSwipeToEnter: Boolean?,
+        isDeviceEntered: Boolean,
+        isUnlocked: Boolean,
+    ): SceneKey =
+        when {
+            canSwipeToEnter == true -> Scenes.Lockscreen
+            !isDeviceEntered -> Scenes.Lockscreen
+            !isUnlocked -> Scenes.Lockscreen
+            else -> Scenes.Gone
+        }
+
+    companion object {
+        val homeScenes =
+            setOf(
+                Scenes.Gone,
+                Scenes.Lockscreen,
+            )
+    }
+}
+
+@Module
+interface HomeSceneFamilyResolverModule {
+    @Binds @IntoSet fun provideSceneResolver(interactor: HomeSceneFamilyResolver): SceneResolver
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt
new file mode 100644
index 0000000..99e554e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/NotifShadeSceneFamilyResolver.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.domain.resolver
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class NotifShadeSceneFamilyResolver
+@Inject
+constructor(
+    @Application applicationScope: CoroutineScope,
+    shadeInteractor: ShadeInteractor,
+) : SceneResolver {
+    override val targetFamily: SceneKey = SceneFamilies.NotifShade
+
+    override val resolvedScene: StateFlow<SceneKey> =
+        shadeInteractor.shadeMode
+            .map(::notifShadeScene)
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = notifShadeScene(shadeInteractor.shadeMode.value),
+            )
+
+    override fun includesScene(scene: SceneKey): Boolean = scene in notifShadeScenes
+
+    private fun notifShadeScene(shadeMode: ShadeMode) =
+        when (shadeMode) {
+            is ShadeMode.Single -> Scenes.Shade
+            is ShadeMode.Dual -> Scenes.NotificationsShade
+            is ShadeMode.Split -> Scenes.Shade
+        }
+
+    companion object {
+        val notifShadeScenes =
+            setOf(
+                Scenes.NotificationsShade,
+                Scenes.Shade,
+            )
+    }
+}
+
+@Module
+interface NotifShadeSceneFamilyResolverModule {
+    @Binds @IntoSet fun bindResolver(interactor: NotifShadeSceneFamilyResolver): SceneResolver
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt
new file mode 100644
index 0000000..2962a3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/QuickSettingsSceneFamilyResolver.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.resolver
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+@SysUISingleton
+class QuickSettingsSceneFamilyResolver
+@Inject
+constructor(
+    @Application applicationScope: CoroutineScope,
+    shadeInteractor: ShadeInteractor,
+) : SceneResolver {
+    override val targetFamily: SceneKey = SceneFamilies.QuickSettings
+
+    override val resolvedScene: StateFlow<SceneKey> =
+        shadeInteractor.shadeMode
+            .map(::quickSettingsScene)
+            .stateIn(
+                applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = quickSettingsScene(shadeInteractor.shadeMode.value),
+            )
+
+    override fun includesScene(scene: SceneKey): Boolean = scene in quickSettingsScenes
+
+    private fun quickSettingsScene(shadeMode: ShadeMode) =
+        when (shadeMode) {
+            is ShadeMode.Single -> Scenes.QuickSettings
+            is ShadeMode.Dual -> Scenes.QuickSettingsShade
+            is ShadeMode.Split -> Scenes.Shade
+        }
+
+    companion object {
+        val quickSettingsScenes =
+            setOf(
+                Scenes.QuickSettings,
+                Scenes.QuickSettingsShade,
+                Scenes.Shade,
+            )
+    }
+}
+
+@Module
+interface QuickSettingsSceneFamilyResolverModule {
+    @Binds @IntoSet fun bindResolver(interactor: QuickSettingsSceneFamilyResolver): SceneResolver
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt
new file mode 100644
index 0000000..8d7247b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/resolver/SceneResolver.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.resolver
+
+import com.android.compose.animation.scene.SceneKey
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.Multibinds
+import kotlinx.coroutines.flow.StateFlow
+
+/** Resolves [concrete scenes][Scenes] from a [scene family][SceneFamilies]. */
+interface SceneResolver {
+    /** The scene family that this resolves. */
+    val targetFamily: SceneKey
+
+    /** The concrete scene that [targetFamily] is currently resolved to. */
+    val resolvedScene: StateFlow<SceneKey>
+
+    /** Returns `true` if [scene] can be resolved from [targetFamily]. */
+    fun includesScene(scene: SceneKey): Boolean
+}
+
+@Module
+interface SceneResolverModule {
+
+    @Multibinds fun resolverSet(): Set<@JvmSuppressWildcards SceneResolver>
+
+    companion object {
+        @Provides
+        fun provideResolverMap(
+            resolverSet: Set<@JvmSuppressWildcards SceneResolver>
+        ): Map<SceneKey, SceneResolver> = resolverSet.associateBy { it.targetFamily }
+    }
+}
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 0304e73..1e689bd 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
@@ -39,6 +39,7 @@
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
 import com.android.systemui.model.SceneContainerPlugin
 import com.android.systemui.model.SysUiState
 import com.android.systemui.model.updateFlags
@@ -81,6 +82,7 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
@@ -120,6 +122,7 @@
     private val uiEventLogger: UiEventLogger,
     private val sceneBackInteractor: SceneBackInteractor,
     private val shadeSessionStorage: SessionStorage,
+    private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor,
 ) : CoreStartable {
     private val centralSurfaces: CentralSurfaces?
         get() = centralSurfacesOptLazy.get().getOrNull()
@@ -227,6 +230,25 @@
         handleDeviceUnlockStatus()
         handlePowerState()
         handleShadeTouchability()
+        handleSurfaceBehindKeyguardVisibility()
+    }
+
+    private fun handleSurfaceBehindKeyguardVisibility() {
+        applicationScope.launch {
+            sceneInteractor.currentScene.collectLatest { currentScene ->
+                if (currentScene == Scenes.Lockscreen) {
+                    // Wait for surface to become visible
+                    windowMgrLockscreenVisInteractor.surfaceBehindVisibility.first { it }
+                    // Make sure the device is actually unlocked before force-changing the scene
+                    deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked }
+                    // Override the current transition, if any, by forcing the scene to Gone
+                    sceneInteractor.changeScene(
+                        toScene = Scenes.Gone,
+                        loggingReason = "surface behind keyguard is visible",
+                    )
+                }
+            }
+        }
     }
 
     private fun handleBouncerImeVisibility() {
@@ -329,8 +351,7 @@
                                 Scenes.Gone to "device was unlocked in Bouncer scene"
                             } else {
                                 val prevScene = previousScene.value
-                                (prevScene
-                                    ?: Scenes.Gone) to
+                                (prevScene ?: Scenes.Gone) to
                                     "device was unlocked in Bouncer scene, from sceneKey=$prevScene"
                             }
                         isOnLockscreen ->
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index 9d6720b..cf1518e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.scene.shared.logger
 
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
@@ -84,6 +85,30 @@
         )
     }
 
+    fun logSceneTransition(transitionState: ObservableTransitionState) {
+        when (transitionState) {
+            is ObservableTransitionState.Transition -> {
+                logBuffer.log(
+                    tag = TAG,
+                    level = LogLevel.INFO,
+                    messageInitializer = {
+                        str1 = transitionState.fromScene.toString()
+                        str2 = transitionState.toScene.toString()
+                    },
+                    messagePrinter = { "Scene transition started: $str1 → $str2" },
+                )
+            }
+            is ObservableTransitionState.Idle -> {
+                logBuffer.log(
+                    tag = TAG,
+                    level = LogLevel.INFO,
+                    messageInitializer = { str1 = transitionState.currentScene.toString() },
+                    messagePrinter = { "Scene transition idle on: $str1" },
+                )
+            }
+        }
+    }
+
     fun logVisibilityChange(
         from: Boolean,
         to: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
index 99118bc..c34a6cd 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneViewModel.kt
@@ -23,7 +23,7 @@
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
@@ -43,39 +43,42 @@
 ) {
     val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
         shadeInteractor.shadeMode
-            .map { shadeMode -> destinationScenes(shadeMode = shadeMode) }
+            .map(::destinationScenes)
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.WhileSubscribed(),
-                initialValue = destinationScenes(shadeMode = shadeInteractor.shadeMode.value)
+                initialValue =
+                    destinationScenes(
+                        shadeMode = shadeInteractor.shadeMode.value,
+                    )
             )
 
-    private fun destinationScenes(shadeMode: ShadeMode): Map<UserAction, UserActionResult> {
+    private fun destinationScenes(
+        shadeMode: ShadeMode,
+    ): Map<UserAction, UserActionResult> {
         return buildMap {
-            if (shadeMode is ShadeMode.Single) {
-                this[
+            if (
+                shadeMode is ShadeMode.Single ||
+                    // TODO(b/338577208): Remove this once we add Dual Shade invocation zones.
+                    shadeMode is ShadeMode.Dual
+            ) {
+                put(
                     Swipe(
                         pointerCount = 2,
                         fromSource = Edge.Top,
                         direction = SwipeDirection.Down,
-                    )] = UserActionResult(Scenes.QuickSettings)
+                    ),
+                    UserActionResult(SceneFamilies.QuickSettings)
+                )
             }
 
-            // TODO(b/338577208): Remove this once we add Dual Shade invocation zones.
-            if (shadeMode is ShadeMode.Dual) {
-                this[
-                    Swipe(
-                        pointerCount = 2,
-                        fromSource = Edge.Top,
-                        direction = SwipeDirection.Down,
-                    )] = UserActionResult(Scenes.QuickSettingsShade)
-            }
-
-            val downSceneKey =
-                if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade
-            val downTransitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
-            this[Swipe(direction = SwipeDirection.Down)] =
-                UserActionResult(downSceneKey, downTransitionKey)
+            put(
+                Swipe(direction = SwipeDirection.Down),
+                UserActionResult(
+                    SceneFamilies.NotifShade,
+                    ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                )
+            )
         }
     }
 }
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 09c80b0..d380251 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
@@ -34,6 +34,8 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /** Models UI state for the scene container. */
@@ -61,7 +63,9 @@
     val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible
 
     private val destinationScenesBySceneKey =
-        scenes.associate { scene -> scene.key to scene.destinationScenes }
+        scenes.associate { scene ->
+            scene.key to scene.destinationScenes.flatMapLatestConflated { replaceSceneFamilies(it) }
+        }
 
     fun currentDestinationScenes(
         scope: CoroutineScope,
@@ -140,7 +144,38 @@
             val fromLockscreenScene = currentScene.value == Scenes.Lockscreen
 
             !fromLockscreenScene || !isFalseTouch
+        } ?: true
+    }
+
+    /**
+     * Immediately resolves any scene families present in [actionResultMap] to their current
+     * resolution target.
+     */
+    fun resolveSceneFamilies(
+        actionResultMap: Map<UserAction, UserActionResult>,
+    ): Map<UserAction, UserActionResult> {
+        return actionResultMap.mapValues { (_, actionResult) ->
+            sceneInteractor.resolveSceneFamilyOrNull(actionResult.toScene)?.value?.let {
+                actionResult.copy(toScene = it)
+            } ?: actionResult
         }
-            ?: true
+    }
+
+    private fun replaceSceneFamilies(
+        destinationScenes: Map<UserAction, UserActionResult>,
+    ): Flow<Map<UserAction, UserActionResult>> {
+        return destinationScenes
+            .mapValues { (_, actionResult) ->
+                sceneInteractor.resolveSceneFamily(actionResult.toScene).map { scene ->
+                    actionResult.copy(toScene = scene)
+                }
+            }
+            .combineValueFlows()
     }
 }
+
+private fun <K, V> Map<K, Flow<V>>.combineValueFlows(): Flow<Map<K, V>> =
+    combine(
+        asIterable().map { (k, fv) -> fv.map { k to it } },
+        transform = Array<Pair<K, V>>::toMap,
+    )
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
index 8a60aeb..d7ddc50 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.ScreenRecordTile
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor.ScreenRecordTileDataInteractor
 import com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor.ScreenRecordTileUserActionInteractor
@@ -48,6 +49,13 @@
     @StringKey(ScreenRecordTile.TILE_SPEC)
     fun bindScreenRecordTile(screenRecordTile: ScreenRecordTile): QSTileImpl<*>
 
+    @Binds
+    @IntoMap
+    @StringKey(SCREEN_RECORD_TILE_SPEC)
+    fun provideScreenRecordAvailabilityInteractor(
+            impl: ScreenRecordTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
     companion object {
         private const val SCREEN_RECORD_TILE_SPEC = "screenrecord"
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
index d59d220..9eeb3b9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt
@@ -28,6 +28,7 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
 
 /**
  * Repository storing information about the state of screen recording.
@@ -38,6 +39,9 @@
 interface ScreenRecordRepository {
     /** The current screen recording state. Note that this is a cold flow. */
     val screenRecordState: Flow<ScreenRecordModel>
+
+    /** Stops the recording. */
+    suspend fun stopRecording()
 }
 
 @SysUISingleton
@@ -90,4 +94,8 @@
             ScreenRecordModel.DoingNothing
         }
     }
+
+    override suspend fun stopRecording() {
+        withContext(bgCoroutineContext) { recordingController.stopRecording() }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
index be39d2e..4bf6c96 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotDetectionController.kt
@@ -18,6 +18,7 @@
 
 import android.content.pm.PackageManager
 import android.content.pm.PackageManager.ComponentInfoFlags
+import android.content.pm.PackageManager.MATCH_ANY_USER
 import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
 import android.view.Display
 import android.view.IWindowManager
@@ -47,7 +48,8 @@
         // Convert component names to app names.
         return components.map {
             packageManager
-                .getActivityInfo(it, ComponentInfoFlags.of(MATCH_DISABLED_COMPONENTS.toLong()))
+                .getActivityInfo(it, ComponentInfoFlags.of(
+                    (MATCH_DISABLED_COMPONENTS or MATCH_ANY_USER).toLong()))
                 .loadLabel(packageManager)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 40d709d..3c3797b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -7,6 +7,7 @@
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import com.android.internal.logging.UiEventLogger
 import com.android.internal.util.ScreenshotRequest
+import com.android.systemui.Flags.screenshotShelfUi2
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.display.data.repository.DisplayRepository
@@ -139,8 +140,8 @@
 
     private suspend fun getDisplaysToScreenshot(requestType: Int): List<Display> {
         val allDisplays = displays.first()
-        return if (requestType == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
-            // If this is a provided image, let's show the UI on the default display only.
+        return if (requestType == TAKE_SCREENSHOT_PROVIDED_IMAGE || screenshotShelfUi2()) {
+            // If this is a provided image or using the shelf UI, just screenshot th default display
             allDisplays.filter { it.displayId == Display.DEFAULT_DISPLAY }
         } else {
             allDisplays.filter { it.type in ALLOWED_DISPLAY_TYPES }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
index 80aa0ef..4f27b9e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/PolicyRequestProcessor.kt
@@ -79,7 +79,10 @@
     }
 
     /** Produce a new [ScreenshotData] using [CaptureParameters] */
-    suspend fun modify(original: ScreenshotData, updates: CaptureParameters): ScreenshotData {
+    private suspend fun modify(
+        original: ScreenshotData,
+        updates: CaptureParameters,
+    ): ScreenshotData {
         // Update and apply bitmap capture depending on the parameters.
         val updated =
             when (val type = updates.type) {
@@ -117,7 +120,7 @@
         return replaceWithScreenshot(
             original = original,
             componentName = topMainRootTask?.topActivity ?: defaultComponent,
-            owner = topMainRootTask?.userId?.let { UserHandle.of(it) } ?: defaultOwner,
+            owner = defaultOwner,
             displayId = original.displayId
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index bf0843b..2def6c7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -157,9 +157,15 @@
     private var anyBouncerShowing = false
 
     /**
-     * True if the shade is fully expanded, meaning the hub should not receive any touch input.
+     * True if the shade is fully expanded and the user is not interacting with it anymore, meaning
+     * the hub should not receive any touch input.
      *
-     * Tracks [ShadeInteractor.isAnyFullyExpanded].
+     * We need to not pause the touch handling lifecycle as soon as the shade opens because if the
+     * user swipes down, then back up without lifting their finger, the lifecycle will be paused
+     * then resumed, and resuming force-stops all active touch sessions. This means the shade will
+     * not receive the end of the gesture and will be stuck open.
+     *
+     * Based on [ShadeInteractor.isAnyFullyExpanded] and [ShadeInteractor.isUserInteracting].
      */
     private var shadeShowing = false
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index 14659e7..f463cb5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -47,6 +47,7 @@
             viewsIdToTranslate =
                 setOf(
                     ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade),
+                    ViewIdToTranslate(R.id.qs_footer_actions, START, filterShade),
                     ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade)),
             progressProvider = progressProvider)
     }
@@ -55,9 +56,8 @@
         UnfoldConstantTranslateAnimator(
             viewsIdToTranslate =
             setOf(
-                ViewIdToTranslate(R.id.statusIcons, END, filterShade),
+                ViewIdToTranslate(R.id.shade_header_system_icons, END, filterShade),
                 ViewIdToTranslate(R.id.privacy_container, END, filterShade),
-                ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade),
                 ViewIdToTranslate(R.id.carrier_group, END, filterShade),
                 ViewIdToTranslate(R.id.clock, START, filterShade),
                 ViewIdToTranslate(R.id.date, START, filterShade)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 3826b50..9624e0f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -25,7 +25,6 @@
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
 import static com.android.systemui.Flags.predictiveBackAnimateShade;
-import static com.android.systemui.Flags.shadeCollapseActivityLaunchFix;
 import static com.android.systemui.Flags.smartspaceRelocateToBottom;
 import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
 import static com.android.systemui.classifier.Classifier.GENERIC;
@@ -484,7 +483,9 @@
     private float mBottomAreaShadeAlpha;
     final ValueAnimator mBottomAreaShadeAlphaAnimator;
     private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
-            NotificationPanelView::setPanelAlphaInternal,
+            (view, alpha) -> {
+                setAlphaInternal(alpha);
+            },
             NotificationPanelView::getCurrentPanelAlpha,
             R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag,
             R.id.panel_alpha_animator_end_tag);
@@ -537,8 +538,6 @@
     private final KeyguardMediaController mKeyguardMediaController;
 
     private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
-    private final Optional<NotificationPanelUnfoldAnimationController>
-            mNotificationPanelUnfoldAnimationController;
 
     /** The drag distance required to fully expand the split shade. */
     private int mSplitShadeFullTransitionDistance;
@@ -964,8 +963,6 @@
 
         mKeyguardUnfoldTransition = unfoldComponent.map(
                 SysUIUnfoldComponent::getKeyguardUnfoldTransition);
-        mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
-                SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
 
         updateUserSwitcherFlags();
         mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
@@ -1131,9 +1128,6 @@
         mShadeHeaderController.init();
         mShadeHeaderController.setShadeCollapseAction(
                 () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f));
-        mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
-        mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
-                controller.setup(mNotificationContainerParent));
 
         // Dreaming->Lockscreen
         collectFlow(
@@ -1511,9 +1505,6 @@
         if (!KeyguardBottomAreaRefactor.isEnabled()) {
             setKeyguardBottomAreaVisibility(mBarState, false);
         }
-
-        mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
-        mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
     }
 
     private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
@@ -1797,6 +1788,7 @@
 
     private void updateKeyguardStatusViewAlignment(boolean animate) {
         boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
+        mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
         if (MigrateClocksToBlueprint.isEnabled()) {
             mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered);
             return;
@@ -1804,7 +1796,6 @@
         ConstraintLayout layout = mNotificationContainerParent;
         mKeyguardStatusViewController.updateAlignment(
                 layout, mSplitShadeEnabled, shouldBeCentered, animate);
-        mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
     }
 
     private boolean shouldKeyguardStatusViewBeCentered() {
@@ -3085,6 +3076,11 @@
         }
     }
 
+    private void setAlphaInternal(float alpha) {
+        mKeyguardInteractor.setPanelAlpha(alpha / 255f);
+        mView.setPanelAlphaInternal(alpha);
+    }
+
     @Override
     public void setAlphaChangeAnimationEndAction(Runnable r) {
         mPanelAlphaEndAction = r;
@@ -4128,11 +4124,7 @@
 
     @Override
     public boolean canBeCollapsed() {
-        return !isFullyCollapsed() && !isTracking() && !isClosing()
-                // Don't try to collapse if on keyguard, as the expansion fraction is 1 in this
-                // case.
-                && !(shadeCollapseActivityLaunchFix() && mExpandedFraction == 1f
-                && mBarState == KEYGUARD);
+        return !isFullyCollapsed() && !isTracking() && !isClosing();
     }
 
     public void instantCollapse() {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 6efa633..e41f99b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -32,6 +32,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.AuthKeyguardMessageArea;
+import com.android.keyguard.KeyguardUnfoldTransition;
 import com.android.keyguard.LockIconViewController;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ActivityTransitionAnimator;
@@ -72,6 +73,7 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.util.time.SystemClock;
 
@@ -138,6 +140,11 @@
     private final PanelExpansionInteractor mPanelExpansionInteractor;
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
 
+    /**
+     * 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.
+     */
+    private boolean mExternalTouchIntercepted = false;
     private boolean mIsTrackingBarGesture = false;
     private boolean mIsOcclusionTransitionRunning = false;
     private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener;
@@ -169,6 +176,7 @@
             DozeScrimController dozeScrimController,
             NotificationShadeWindowController controller,
             Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
+            Optional<SysUIUnfoldComponent> unfoldComponent,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             NotificationInsetsController notificationInsetsController,
             AmbientState ambientState,
@@ -222,13 +230,21 @@
         bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
 
         collectFlow(mView, keyguardTransitionInteractor.transition(
-                Edge.Companion.create(LOCKSCREEN, DREAMING)),
+                Edge.create(LOCKSCREEN, DREAMING)),
                 mLockscreenToDreamingTransition);
         collectFlow(
                 mView,
                 notificationLaunchAnimationInteractor.isLaunchAnimationRunning(),
                 this::setExpandAnimationRunning);
 
+        var keyguardUnfoldTransition = unfoldComponent.map(
+                SysUIUnfoldComponent::getKeyguardUnfoldTransition);
+        var notificationPanelUnfoldAnimationController = unfoldComponent.map(
+                SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
+
+        keyguardUnfoldTransition.ifPresent(KeyguardUnfoldTransition::setup);
+        notificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
+
         mClock = clock;
         if (featureFlagsClassic.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) {
             unfoldTransitionProgressProvider.ifPresent(
@@ -255,11 +271,28 @@
     }
 
     /**
-     * Handle a touch event while dreaming by forwarding the event to the content view.
+     * Handle a touch event while dreaming or on the hub by forwarding the event to the content
+     * view.
+     * <p>
+     * Since important logic for handling touches lives in the dispatch/intercept phases, we
+     * simulate going through all of these stages before sending onTouchEvent if intercepted.
+     *
      * @param event The event to forward.
      */
-    public void handleDreamTouch(MotionEvent event) {
-        mView.dispatchTouchEvent(event);
+    public void handleExternalTouch(MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            mExternalTouchIntercepted = false;
+        }
+
+        if (!mView.dispatchTouchEvent(event)) {
+            return;
+        }
+        if (!mExternalTouchIntercepted) {
+            mExternalTouchIntercepted = mView.onInterceptTouchEvent(event);
+        }
+        if (mExternalTouchIntercepted) {
+            mView.onTouchEvent(event);
+        }
     }
 
     /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 884ccef..004db16 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -17,21 +17,16 @@
 package com.android.systemui.shade
 
 import android.view.MotionEvent
-import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.dagger.ShadeTouchLog
 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.SlightlyFasterShadeCollapse
 import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.VibratorHelper
@@ -61,10 +56,7 @@
     @Background private val scope: CoroutineScope,
     private val shadeInteractor: ShadeInteractor,
     private val sceneInteractor: SceneInteractor,
-    private val deviceEntryInteractor: DeviceEntryInteractor,
-    private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
     private val notificationStackScrollLayout: NotificationStackScrollLayout,
-    @ShadeTouchLog private val touchLog: LogBuffer,
     private val vibratorHelper: VibratorHelper,
     commandQueue: CommandQueue,
     statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
@@ -100,7 +92,7 @@
 
     override fun instantCollapseShade() {
         sceneInteractor.snapToScene(
-            getCollapseDestinationScene(),
+            SceneFamilies.Home,
             "hide shade",
         )
     }
@@ -140,24 +132,12 @@
 
     private fun animateCollapseShadeInternal() {
         sceneInteractor.changeScene(
-            getCollapseDestinationScene(), // TODO(b/336581871): add sceneState?
+            SceneFamilies.Home, // TODO(b/336581871): add sceneState?
             "ShadeController.animateCollapseShade",
             SlightlyFasterShadeCollapse,
         )
     }
 
-    private fun getCollapseDestinationScene(): SceneKey {
-        // Always check whether device is unlocked before transitioning to gone scene.
-        return if (
-            deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked &&
-                deviceEntryInteractor.isDeviceEntered.value
-        ) {
-            Scenes.Gone
-        } else {
-            Scenes.Lockscreen
-        }
-    }
-
     override fun cancelExpansionAndCollapseShade() {
         // TODO do we need to actually cancel the touch session?
         animateCollapseShade()
@@ -192,19 +172,14 @@
     }
 
     override fun expandToNotifications() {
-        val shadeMode = shadeInteractor.shadeMode.value
         sceneInteractor.changeScene(
-            if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
-            "ShadeController.animateExpandShade"
+            SceneFamilies.NotifShade,
+            "ShadeController.animateExpandShade",
         )
     }
 
     override fun expandToQs() {
-        val shadeMode = shadeInteractor.shadeMode.value
-        sceneInteractor.changeScene(
-            if (shadeMode is ShadeMode.Dual) Scenes.QuickSettingsShade else Scenes.QuickSettings,
-            "ShadeController.animateExpandQs"
-        )
+        sceneInteractor.changeScene(SceneFamilies.QuickSettings, "ShadeController.animateExpandQs")
     }
 
     override fun setVisibilityListener(listener: ShadeVisibilityListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
index 55bd8c6..3a483f4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 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.shade.shared.model.ShadeMode
 import javax.inject.Inject
@@ -36,11 +37,7 @@
         if (shadeInteractor.isQsExpanded.value) {
             val key =
                 if (fullyCollapse || shadeInteractor.shadeMode.value is ShadeMode.Dual) {
-                    if (deviceEntryInteractor.isDeviceEntered.value) {
-                        Scenes.Gone
-                    } else {
-                        Scenes.Lockscreen
-                    }
+                    SceneFamilies.Home
                 } else {
                     Scenes.Shade
                 }
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 fe16fc0..d5b4f4d 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,25 +21,23 @@
 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.model.Scenes
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /** ShadeInteractor implementation for Scene Container. */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class ShadeInteractorSceneContainerImpl
 @Inject
@@ -52,10 +50,11 @@
     override val shadeMode: StateFlow<ShadeMode> = shadeRepository.shadeMode
 
     override val shadeExpansion: StateFlow<Float> =
-        sceneBasedExpansion(sceneInteractor, notificationsScene)
+        sceneBasedExpansion(sceneInteractor, SceneFamilies.NotifShade)
             .stateIn(scope, SharingStarted.Eagerly, 0f)
 
-    private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, quickSettingsScene)
+    private val sceneBasedQsExpansion =
+        sceneBasedExpansion(sceneInteractor, SceneFamilies.QuickSettings)
 
     override val qsExpansion: StateFlow<Float> =
         combine(
@@ -78,23 +77,38 @@
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isQsBypassingShade: Flow<Boolean> =
-        sceneInteractor.transitionState
-            .map { state ->
-                when (state) {
-                    is ObservableTransitionState.Idle -> false
-                    is ObservableTransitionState.Transition ->
-                        state.toScene == quickSettingsScene && state.fromScene != notificationsScene
-                }
+        combine(
+                sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
+                sceneInteractor.resolveSceneFamily(SceneFamilies.NotifShade),
+                ::Pair
+            )
+            .flatMapLatestConflated { (quickSettingsScene, notificationsScene) ->
+                sceneInteractor.transitionState
+                    .map { state ->
+                        when (state) {
+                            is ObservableTransitionState.Idle -> false
+                            is ObservableTransitionState.Transition ->
+                                state.toScene == quickSettingsScene &&
+                                    state.fromScene != notificationsScene
+                        }
+                    }
+                    .distinctUntilChanged()
             }
             .distinctUntilChanged()
 
     override val isQsFullscreen: Flow<Boolean> =
-        sceneInteractor.transitionState
-            .map { state ->
-                when (state) {
-                    is ObservableTransitionState.Idle -> state.currentScene == quickSettingsScene
-                    is ObservableTransitionState.Transition -> false
-                }
+        sceneInteractor
+            .resolveSceneFamily(SceneFamilies.QuickSettings)
+            .flatMapLatestConflated { quickSettingsScene ->
+                sceneInteractor.transitionState
+                    .map { state ->
+                        when (state) {
+                            is ObservableTransitionState.Idle ->
+                                state.currentScene == quickSettingsScene
+                            is ObservableTransitionState.Transition -> false
+                        }
+                    }
+                    .distinctUntilChanged()
             }
             .distinctUntilChanged()
 
@@ -108,34 +122,39 @@
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isUserInteractingWithShade: Flow<Boolean> =
-        sceneBasedInteracting(sceneInteractor, notificationsScene)
+        sceneBasedInteracting(sceneInteractor, SceneFamilies.NotifShade)
 
     override val isUserInteractingWithQs: Flow<Boolean> =
-        sceneBasedInteracting(sceneInteractor, quickSettingsScene)
+        sceneBasedInteracting(sceneInteractor, SceneFamilies.QuickSettings)
 
     /**
      * Returns a flow that uses scene transition progress to and from a scene that is pulled down
      * from the top of the screen to a 0-1 expansion amount float.
      */
     fun sceneBasedExpansion(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
-        sceneInteractor.transitionState
-            .flatMapLatest { state ->
-                when (state) {
-                    is ObservableTransitionState.Idle ->
-                        if (state.currentScene == sceneKey) {
-                            flowOf(1f)
-                        } else {
-                            flowOf(0f)
+        sceneInteractor
+            .resolveSceneFamily(sceneKey)
+            .flatMapLatestConflated { resolvedSceneKey ->
+                sceneInteractor.transitionState
+                    .flatMapLatestConflated { state ->
+                        when (state) {
+                            is ObservableTransitionState.Idle ->
+                                if (state.currentScene == resolvedSceneKey) {
+                                    flowOf(1f)
+                                } else {
+                                    flowOf(0f)
+                                }
+                            is ObservableTransitionState.Transition ->
+                                if (state.toScene == resolvedSceneKey) {
+                                    state.progress
+                                } else if (state.fromScene == resolvedSceneKey) {
+                                    state.progress.map { progress -> 1 - progress }
+                                } else {
+                                    flowOf(0f)
+                                }
                         }
-                    is ObservableTransitionState.Transition ->
-                        if (state.toScene == sceneKey) {
-                            state.progress
-                        } else if (state.fromScene == sceneKey) {
-                            state.progress.map { progress -> 1 - progress }
-                        } else {
-                            flowOf(0f)
-                        }
-                }
+                    }
+                    .distinctUntilChanged()
             }
             .distinctUntilChanged()
 
@@ -145,29 +164,16 @@
      */
     fun sceneBasedInteracting(sceneInteractor: SceneInteractor, sceneKey: SceneKey) =
         sceneInteractor.transitionState
-            .map { state ->
+            .flatMapLatestConflated { state ->
                 when (state) {
-                    is ObservableTransitionState.Idle -> false
+                    is ObservableTransitionState.Idle -> flowOf(false)
                     is ObservableTransitionState.Transition ->
-                        state.isInitiatedByUserInput &&
-                            (state.toScene == sceneKey || state.fromScene == sceneKey)
+                        sceneInteractor.resolveSceneFamily(sceneKey).map { resolvedSceneKey ->
+                            state.isInitiatedByUserInput &&
+                                (state.toScene == resolvedSceneKey ||
+                                    state.fromScene == resolvedSceneKey)
+                        }
                 }
             }
             .distinctUntilChanged()
-
-    private val notificationsScene: SceneKey
-        get() =
-            if (shadeMode.value is ShadeMode.Dual) {
-                Scenes.NotificationsShade
-            } else {
-                Scenes.Shade
-            }
-
-    private val quickSettingsScene: SceneKey
-        get() =
-            if (shadeMode.value is ShadeMode.Dual) {
-                Scenes.QuickSettingsShade
-            } else {
-                Scenes.QuickSettings
-            }
 }
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 e7fc18e..d5953ca 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
@@ -17,21 +17,23 @@
 package com.android.systemui.shade.domain.interactor
 
 import com.android.keyguard.LockIconViewController
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 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
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 class ShadeLockscreenInteractorImpl
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
+    @Main private val mainDispatcher: CoroutineDispatcher,
     @Background private val backgroundScope: CoroutineScope,
     private val shadeInteractor: ShadeInteractor,
     private val sceneInteractor: SceneInteractor,
@@ -69,10 +71,11 @@
     override fun setPulsing(pulsing: Boolean) {
         // Now handled elsewhere. Do nothing.
     }
+
     override fun transitionToExpandedShade(delay: Long) {
         backgroundScope.launch {
             delay(delay)
-            changeToShadeScene()
+            withContext(mainDispatcher) { changeToShadeScene() }
         }
     }
 
@@ -98,12 +101,9 @@
     }
 
     private fun changeToShadeScene() {
-        applicationScope.launch {
-            val shadeMode = shadeInteractor.shadeMode.value
-            sceneInteractor.changeScene(
-                if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
-                "ShadeLockscreenInteractorImpl.expandToNotifications",
-            )
-        }
+        sceneInteractor.changeScene(
+            SceneFamilies.NotifShade,
+            "ShadeLockscreenInteractorImpl.expandToNotifications",
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
index b8dd628..0314091 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
@@ -19,49 +19,41 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 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 javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
 /**
  * Models UI state and handles user input for the overlay shade UI, which shows a shade as an
  * overlay on top of another scene UI.
  */
-@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class OverlayShadeViewModel
 @Inject
 constructor(
-    @Application private val applicationScope: CoroutineScope,
+    @Application applicationScope: CoroutineScope,
     private val sceneInteractor: SceneInteractor,
-    deviceEntryInteractor: DeviceEntryInteractor,
 ) {
     /** The scene to show in the background when the overlay shade is open. */
     val backgroundScene: StateFlow<SceneKey> =
-        deviceEntryInteractor.isDeviceEntered
-            .map(::backgroundScene)
+        sceneInteractor
+            .resolveSceneFamily(SceneFamilies.Home)
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.WhileSubscribed(),
-                initialValue = backgroundScene(deviceEntryInteractor.isDeviceEntered.value)
+                initialValue = Scenes.Lockscreen,
             )
 
     /** Notifies that the user has clicked the semi-transparent background scrim. */
     fun onScrimClicked() {
         sceneInteractor.changeScene(
-            toScene = backgroundScene.value,
+            toScene = SceneFamilies.Home,
             loggingReason = "Shade scrim clicked",
         )
     }
-
-    private fun backgroundScene(isDeviceEntered: Boolean): SceneKey {
-        return if (isDeviceEntered) Scenes.Gone else Scenes.Lockscreen
-    }
 }
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 6c76061..b2e0cd0 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
@@ -30,6 +30,9 @@
 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.shade.domain.interactor.PrivacyChipInteractor
 import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -57,6 +60,7 @@
     @Application private val applicationScope: CoroutineScope,
     context: Context,
     private val activityStarter: ActivityStarter,
+    private val sceneInteractor: SceneInteractor,
     shadeInteractor: ShadeInteractor,
     mobileIconsInteractor: MobileIconsInteractor,
     val mobileIconsViewModel: MobileIconsViewModel,
@@ -139,6 +143,15 @@
         clockInteractor.launchClockActivity()
     }
 
+    /** Notifies that the system icons container was clicked. */
+    fun onSystemIconContainerClicked() {
+        sceneInteractor.changeScene(
+            SceneFamilies.Home,
+            "ShadeHeaderViewModel.onSystemIconContainerClicked",
+            TransitionKeys.SlightlyFasterShadeCollapse,
+        )
+    }
+
     /** Notifies that the shadeCarrierGroup was clicked. */
     fun onShadeCarrierGroupClicked() {
         activityStarter.postStartActivityDismissingKeyguard(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
index e4a2424..b0100b9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt
@@ -26,12 +26,12 @@
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.qs.FooterActionsController
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 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.ToSplitShade
 import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
@@ -39,6 +39,7 @@
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import java.util.concurrent.atomic.AtomicBoolean
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -47,6 +48,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -56,7 +58,6 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    deviceEntryInteractor: DeviceEntryInteractor,
     val qsSceneAdapter: QSSceneAdapter,
     val shadeHeaderViewModel: ShadeHeaderViewModel,
     val notifications: NotificationsPlaceholderViewModel,
@@ -70,16 +71,12 @@
 ) {
     val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
         combine(
-                deviceEntryInteractor.isUnlocked,
-                deviceEntryInteractor.canSwipeToEnter,
                 shadeInteractor.shadeMode,
-                qsSceneAdapter.isCustomizerShowing
-            ) { isUnlocked, canSwipeToDismiss, shadeMode, isCustomizerShowing ->
+                qsSceneAdapter.isCustomizerShowing,
+            ) { shadeMode, isCustomizerShowing ->
                 destinationScenes(
-                    isUnlocked = isUnlocked,
-                    canSwipeToDismiss = canSwipeToDismiss,
                     shadeMode = shadeMode,
-                    isCustomizing = isCustomizerShowing
+                    isCustomizing = isCustomizerShowing,
                 )
             }
             .stateIn(
@@ -87,8 +84,6 @@
                 started = SharingStarted.WhileSubscribed(),
                 initialValue =
                     destinationScenes(
-                        isUnlocked = deviceEntryInteractor.isUnlocked.value,
-                        canSwipeToDismiss = deviceEntryInteractor.canSwipeToEnter.value,
                         shadeMode = shadeInteractor.shadeMode.value,
                         isCustomizing = qsSceneAdapter.isCustomizerShowing.value,
                     ),
@@ -100,6 +95,9 @@
     /** Whether or not the shade container should be clickable. */
     val isClickable: StateFlow<Boolean> =
         upDestinationSceneKey
+            .flatMapLatestConflated { key ->
+                key?.let { sceneInteractor.resolveSceneFamily(key) } ?: flowOf(null)
+            }
             .map { it == Scenes.Lockscreen }
             .stateIn(
                 scope = applicationScope,
@@ -138,27 +136,22 @@
     }
 
     private fun destinationScenes(
-        isUnlocked: Boolean,
-        canSwipeToDismiss: Boolean?,
         shadeMode: ShadeMode,
         isCustomizing: Boolean,
     ): Map<UserAction, UserActionResult> {
-        val up =
-            when {
-                canSwipeToDismiss == true -> Scenes.Lockscreen
-                isUnlocked -> Scenes.Gone
-                else -> Scenes.Lockscreen
-            }
-
-        val upTransitionKey = ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
-
-        val down = Scenes.QuickSettings.takeIf { shadeMode is ShadeMode.Single }
-
         return buildMap {
             if (!isCustomizing) {
-                this[Swipe(SwipeDirection.Up)] = UserActionResult(up, upTransitionKey)
+                set(
+                    Swipe(SwipeDirection.Up),
+                    UserActionResult(
+                        SceneFamilies.Home,
+                        ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                    )
+                )
             } // TODO(b/330200163) Add an else to be able to collapse the shade while customizing
-            down?.let { this[Swipe(SwipeDirection.Down)] = UserActionResult(down) }
+            if (shadeMode is ShadeMode.Single) {
+                set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings))
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
index 6d951bf..abffd3c 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
@@ -18,8 +18,10 @@
 import android.app.ActivityOptions
 import android.app.PendingIntent
 import android.content.Intent
+import android.os.Handler
 import android.view.View
 import android.view.ViewGroup
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_DREAM
@@ -51,19 +53,20 @@
 
         @Provides
         fun providesSmartspaceView(
-            activityStarter: ActivityStarter,
-            falsingManager: FalsingManager,
-            parent: ViewGroup,
-            @Named(PLUGIN) plugin: BcSmartspaceDataPlugin,
-            viewWithCustomLayout: View?,
-            onAttachListener: View.OnAttachStateChangeListener
-        ):
-                BcSmartspaceDataPlugin.SmartspaceView {
+                activityStarter: ActivityStarter,
+                falsingManager: FalsingManager,
+                parent: ViewGroup,
+                @Named(PLUGIN) plugin: BcSmartspaceDataPlugin,
+                viewWithCustomLayout: View?,
+                onAttachListener: View.OnAttachStateChangeListener,
+                @Background bgHandler: Handler,
+        ): BcSmartspaceDataPlugin.SmartspaceView {
             val ssView = viewWithCustomLayout
                     as? BcSmartspaceDataPlugin.SmartspaceView
                     ?: plugin.getView(parent)
             // Currently, this is only used to provide SmartspaceView on Dream surface.
             ssView.setUiSurface(UI_SURFACE_DREAM)
+            ssView.setBgHandler(bgHandler)
             ssView.registerDataProvider(plugin)
 
             ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 2dfc920..abf258c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -24,6 +24,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.ViewRootImpl.CLIENT_IMMERSIVE_CONFIRMATION;
 import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
 
@@ -36,12 +37,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Binder;
 import android.os.Bundle;
@@ -71,7 +71,7 @@
 import android.view.animation.Interpolator;
 import android.widget.Button;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
+import android.widget.RelativeLayout;
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.res.R;
@@ -263,6 +263,7 @@
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                 PixelFormat.TRANSLUCENT);
         lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
+        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         // Trusted overlay so touches outside the touchable area are allowed to pass through
         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
                 | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
@@ -275,13 +276,20 @@
 
     private FrameLayout.LayoutParams getBubbleLayoutParams() {
         return new FrameLayout.LayoutParams(
-                mSysUiContext.getResources().getDimensionPixelSize(
-                        R.dimen.immersive_mode_cling_width),
+                getClingWindowWidth(),
                 ViewGroup.LayoutParams.WRAP_CONTENT,
                 Gravity.CENTER_HORIZONTAL | Gravity.TOP);
     }
 
     /**
+     * Returns the width of the cling window.
+     */
+    private int getClingWindowWidth() {
+        return mSysUiContext.getResources().getDimensionPixelSize(
+                R.dimen.immersive_mode_cling_width);
+    }
+
+    /**
      * @return the window token that's used by all ImmersiveModeConfirmation windows.
      */
     IBinder getWindowToken() {
@@ -409,21 +417,6 @@
             mClingLayout = (ViewGroup)
                     View.inflate(mSysUiContext, R.layout.immersive_mode_cling, null);
 
-            TypedArray ta = mDisplayContext.obtainStyledAttributes(
-                    new int[]{android.R.attr.colorAccent});
-            int colorAccent = ta.getColor(0, 0);
-            ta.recycle();
-            mClingLayout.setBackgroundColor(colorAccent);
-            ImageView expandMore = mClingLayout.findViewById(R.id.immersive_cling_ic_expand_more);
-            if (expandMore != null) {
-                expandMore.setImageTintList(ColorStateList.valueOf(colorAccent));
-            }
-            ImageView lightBgCirc = mClingLayout.findViewById(R.id.immersive_cling_back_bg_light);
-            if (lightBgCirc != null) {
-                // Set transparency to 50%
-                lightBgCirc.setImageAlpha(128);
-            }
-
             final Button ok = mClingLayout.findViewById(R.id.ok);
             ok.setOnClickListener(new OnClickListener() {
                 @Override
@@ -482,6 +475,24 @@
 
         @Override
         public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+            // If the top display cutout overlaps with the full-width (windowWidth=-1)/centered
+            // dialog, then adjust the dialog contents by the cutout
+            final int width = getWidth();
+            final int windowWidth = getClingWindowWidth();
+            final Rect topDisplayCutout = insets.getDisplayCutout() != null
+                    ? insets.getDisplayCutout().getBoundingRectTop()
+                    : new Rect();
+            final boolean intersectsTopCutout = topDisplayCutout.intersects(
+                    width - (windowWidth / 2), 0,
+                    width + (windowWidth / 2), topDisplayCutout.bottom);
+            if (mClingWindow != null &&
+                    (windowWidth < 0 || (width > 0 && intersectsTopCutout))) {
+                final View iconView = mClingWindow.findViewById(R.id.immersive_cling_icon);
+                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
+                        iconView.getLayoutParams();
+                lp.topMargin = topDisplayCutout.bottom;
+                iconView.setLayoutParams(lp);
+            }
             // we will be hiding the nav bar, so layout as if it's already hidden
             return new WindowInsets.Builder(insets).setInsets(
                     Type.systemBars(), Insets.NONE).build();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 5268009..342ec0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -23,10 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.AppGlobals;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.UserSwitchObserver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -151,13 +148,6 @@
     private KeyCharacterMap mKeyCharacterMap;
     private KeyCharacterMap mBackupKeyCharacterMap;
 
-    private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
-            @Override
-            public void onUserSwitching(int newUserId) throws RemoteException {
-                dismiss();
-            }
-    };
-
     @VisibleForTesting
     KeyboardShortcutListSearch(Context context, WindowManager windowManager) {
         this.mContext = new ContextThemeWrapper(
@@ -474,14 +464,6 @@
             mBackgroundHandler = new Handler(mHandlerThread.getLooper());
         }
 
-        if (validateKeyboardShortcutHelperIconUri()) {
-            try {
-                ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
-            } catch (RemoteException e) {
-                Log.e(TAG, "could not register user switch observer", e);
-            }
-        }
-
         retrieveKeyCharacterMap(deviceId);
         mAppShortcutsReceived = false;
         mImeShortcutsReceived = false;
@@ -563,13 +545,6 @@
             mKeyboardShortcutsBottomSheetDialog = null;
         }
         mHandlerThread.quit();
-        if (validateKeyboardShortcutHelperIconUri()) {
-            try {
-                ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not unregister user switch observer", e);
-            }
-        }
     }
 
     private KeyboardShortcutMultiMappingGroup getMultiMappingSystemShortcuts(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index c742f641..6ee13c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -24,12 +24,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
 import android.app.Dialog;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.UserSwitchObserver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -137,13 +134,6 @@
     @Nullable private List<KeyboardShortcutGroup> mReceivedAppShortcutGroups = null;
     @Nullable private List<KeyboardShortcutGroup> mReceivedImeShortcutGroups = null;
 
-    private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
-            @Override
-            public void onUserSwitching(int newUserId) throws RemoteException {
-                dismiss();
-            }
-    };
-
     @VisibleForTesting
     KeyboardShortcuts(Context context, WindowManager windowManager) {
         this.mContext = new ContextThemeWrapper(
@@ -394,14 +384,6 @@
             mBackgroundHandler = new Handler(mHandlerThread.getLooper());
         }
 
-        if (validateKeyboardShortcutHelperIconUri()) {
-            try {
-                ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
-            } catch (RemoteException e) {
-                Log.e(TAG, "could not register user switch observer", e);
-            }
-        }
-
         retrieveKeyCharacterMap(deviceId);
 
         mReceivedAppShortcutGroups = null;
@@ -455,7 +437,7 @@
         if (mReceivedAppShortcutGroups == null || mReceivedImeShortcutGroups == null) {
             return;
         }
-        List<KeyboardShortcutGroup> shortcutGroups = mReceivedAppShortcutGroups;
+        List<KeyboardShortcutGroup> shortcutGroups = new ArrayList<>(mReceivedAppShortcutGroups);
         shortcutGroups.addAll(mReceivedImeShortcutGroups);
         mReceivedAppShortcutGroups = null;
         mReceivedImeShortcutGroups = null;
@@ -476,13 +458,6 @@
             mKeyboardShortcutsDialog = null;
         }
         mHandlerThread.quit();
-        if (validateKeyboardShortcutHelperIconUri()) {
-            try {
-                ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not unregister user switch observer", e);
-            }
-        }
     }
 
     private KeyboardShortcutGroup getSystemShortcuts() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 47939ae..95cabfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -20,6 +20,7 @@
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
 import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START;
 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
 import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
 import static android.hardware.biometrics.BiometricSourceType.FACE;
@@ -1296,6 +1297,12 @@
         @Override
         public void onBiometricAcquired(BiometricSourceType biometricSourceType, int acquireInfo) {
             if (biometricSourceType == FACE) {
+                if (acquireInfo == FACE_ACQUIRED_START) {
+                    // Let's hide any previous messages when authentication starts, otherwise
+                    // multiple auth attempts would overlap.
+                    hideBiometricMessage();
+                    mBiometricErrorMessageToShowOnScreenOn = null;
+                }
                 mFaceAcquiredMessageDeferral.processFrame(acquireInfo);
             }
         }
@@ -1485,13 +1492,6 @@
         @Override
         public void onBiometricRunningStateChanged(boolean running,
                 BiometricSourceType biometricSourceType) {
-            if (running && biometricSourceType == FACE) {
-                // Let's hide any previous messages when authentication starts, otherwise
-                // multiple auth attempts would overlap.
-                hideBiometricMessage();
-                mBiometricErrorMessageToShowOnScreenOn = null;
-            }
-
             if (!running && biometricSourceType == FACE) {
                 showTrustAgentErrorMessage(mTrustAgentErrorMessage);
             }
@@ -1630,6 +1630,7 @@
                         "skip showing FACE_ERROR_TIMEOUT due to co-ex logic");
             }
         } else if (deferredFaceMessage != null) {
+            mBouncerMessageInteractor.setFaceAcquisitionMessage(deferredFaceMessage.toString());
             // Face-only: The face timeout message is not very actionable, let's ask the
             // user to manually retry.
             showBiometricMessage(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 6d34a0f..04a413a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -303,7 +303,12 @@
         // TODO(b/169655907): get the semi-filtered notifications for current user
         Collection<NotificationEntry> allNotifications = mNotifPipeline.getAllNotifs();
         if (notificationMediaManagerBackgroundExecution()) {
-            mBackgroundExecutor.execute(() -> findPlayingMediaNotification(allNotifications));
+            // Create new sbn list to be accessed in background thread.
+            List<StatusBarNotification> statusBarNotifications = new ArrayList<>();
+            for (NotificationEntry entry: allNotifications) {
+                statusBarNotifications.add(entry.getSbn());
+            }
+            mBackgroundExecutor.execute(() -> findPlayingMediaNotification(statusBarNotifications));
         } else {
             findPlayingMediaNotification(allNotifications);
         }
@@ -341,6 +346,51 @@
             }
         }
 
+        StatusBarNotification statusBarNotification = null;
+        if (mediaNotification != null) {
+            statusBarNotification = mediaNotification.getSbn();
+        }
+        setUpControllerAndKey(controller, statusBarNotification);
+    }
+
+    /**
+     * Find a notification and media controller associated with the playing media session, and
+     * update this manager's internal state.
+     * This method must be called in background.
+     * TODO(b/273443374) check this method
+     */
+    void findPlayingMediaNotification(@NonNull List<StatusBarNotification> allNotifications) {
+        // Promote the media notification with a controller in 'playing' state, if any.
+        StatusBarNotification statusBarNotification = null;
+        MediaController controller = null;
+        for (StatusBarNotification sbn : allNotifications) {
+            Notification notif = sbn.getNotification();
+            if (notif.isMediaNotification()) {
+                final MediaSession.Token token =
+                        sbn.getNotification().extras.getParcelable(
+                                Notification.EXTRA_MEDIA_SESSION, MediaSession.Token.class);
+                if (token != null) {
+                    MediaController aController = new MediaController(mContext, token);
+                    if (PlaybackState.STATE_PLAYING
+                            == getMediaControllerPlaybackState(aController)) {
+                        if (DEBUG_MEDIA) {
+                            Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
+                                    + sbn.getKey());
+                        }
+                        statusBarNotification = sbn;
+                        controller = aController;
+                        break;
+                    }
+                }
+            }
+        }
+
+        setUpControllerAndKey(controller, statusBarNotification);
+    }
+
+    private void setUpControllerAndKey(
+            MediaController controller,
+            StatusBarNotification mediaNotification) {
         if (controller != null && !sameSessions(mMediaController, controller)) {
             // We have a new media session
             clearCurrentMediaNotificationSession();
@@ -354,8 +404,8 @@
         }
 
         if (mediaNotification != null
-                && !mediaNotification.getSbn().getKey().equals(mMediaNotificationKey)) {
-            mMediaNotificationKey = mediaNotification.getSbn().getKey();
+                && !mediaNotification.getKey().equals(mMediaNotificationKey)) {
+            mMediaNotificationKey = mediaNotification.getKey();
             if (DEBUG_MEDIA) {
                 Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
                         + mMediaNotificationKey);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 9a82ecf..855798c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -40,6 +40,7 @@
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.animation.ShadeInterpolation;
 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.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -93,6 +94,7 @@
     private float mCornerAnimationDistance;
     private float mActualWidth = -1;
     private int mMaxIconsOnLockscreen;
+    private int mNotificationScrimPadding;
     private boolean mCanModifyColorOfNotifications;
     private boolean mCanInteract;
     private NotificationStackScrollLayout mHostLayout;
@@ -136,6 +138,7 @@
         mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
         mMaxIconsOnLockscreen = res.getInteger(R.integer.max_notif_icons_on_lockscreen);
+        mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
 
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
         final int newShelfHeight = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
@@ -261,17 +264,33 @@
             viewState.hasItemsInStableShelf = false;
         }
 
-        final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
+        final float stackBottom = SceneContainerFlag.isEnabled()
+                ? getStackBottom(ambientState)
+                : ambientState.getStackY() + ambientState.getStackHeight();
+
         if (viewState.hidden) {
             // if the shelf is hidden, position it at the end of the stack (plus the clip
             // padding), such that when it appears animated, it will smoothly move in from the
             // bottom, without jump cutting any notifications
-            viewState.setYTranslation(stackEnd + mPaddingBetweenElements);
+            viewState.setYTranslation(stackBottom + mPaddingBetweenElements);
         } else {
-            viewState.setYTranslation(stackEnd - viewState.height);
+            viewState.setYTranslation(stackBottom - viewState.height);
         }
     }
 
+    /**
+     * bottom-most position, where we can draw the stack
+     */
+    private float getStackBottom(AmbientState ambientState) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+        float stackBottom = ambientState.getStackCutoff() - mNotificationScrimPadding;
+        if (ambientState.isExpansionChanging()) {
+            stackBottom = MathUtils.lerp(stackBottom * StackScrollAlgorithm.START_FRACTION,
+                    stackBottom, ambientState.getExpansionFraction());
+        }
+        return stackBottom;
+    }
+
     private int getSpeedBumpIndex() {
         NotificationIconContainerRefactor.assertInLegacyMode();
         return mHostLayout.getSpeedBumpIndex();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index f8193a4..d0702fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,6 +27,7 @@
 import android.app.Notification;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -35,6 +36,7 @@
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Trace;
@@ -94,6 +96,8 @@
     public static final int STATE_DOT = 1;
     public static final int STATE_HIDDEN = 2;
 
+    public static final float APP_ICON_SCALE = .75f;
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({STATE_ICON, STATE_DOT, STATE_HIDDEN})
     public @interface VisibleState { }
@@ -499,7 +503,12 @@
             userId = UserHandle.USER_SYSTEM;
         }
 
-        Drawable icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
+        // Try to load the monochrome app icon if applicable
+        Drawable icon = maybeGetMonochromeAppIcon(context, statusBarIcon);
+        // Otherwise, just use the icon normally
+        if (icon == null) {
+            icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
+        }
 
         TypedValue typedValue = new TypedValue();
         sysuiContext.getResources().getValue(R.dimen.status_bar_icon_scale_factor,
@@ -526,6 +535,26 @@
         return new ScalingDrawableWrapper(icon, scaleFactor);
     }
 
+    @Nullable
+    private Drawable maybeGetMonochromeAppIcon(Context context,
+            StatusBarIcon statusBarIcon) {
+        if (android.app.Flags.notificationsUseMonochromeAppIcon()
+                && statusBarIcon.type == StatusBarIcon.Type.MaybeMonochromeAppIcon) {
+            // Check if we have a monochrome app icon
+            PackageManager pm = context.getPackageManager();
+            Drawable appIcon = context.getApplicationInfo().loadIcon(pm);
+            if (appIcon instanceof AdaptiveIconDrawable) {
+                Drawable monochrome = ((AdaptiveIconDrawable) appIcon).getMonochrome();
+                if (monochrome != null) {
+                    setCropToPadding(true);
+                    setScaleType(ScaleType.CENTER);
+                    return new ScalingDrawableWrapper(monochrome, APP_ICON_SCALE);
+                }
+            }
+        }
+        return null;
+    }
+
     public StatusBarIcon getStatusBarIcon() {
         return mIcon;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
index c57cf69..7e9acaf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
@@ -17,16 +17,15 @@
 package com.android.systemui.statusbar.chips.call.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
 import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
 
 /** Interactor for the ongoing phone call chip shown in the status bar. */
 @SysUISingleton
-open class CallChipInteractor @Inject constructor() : OngoingActivityChipInteractor {
-    // TODO(b/332662551): Implement this flow.
-    override val chip: StateFlow<OngoingActivityChipModel> =
-        MutableStateFlow(OngoingActivityChipModel.Hidden)
+class CallChipInteractor
+@Inject
+constructor(
+    repository: OngoingCallRepository,
+) {
+    val ongoingCallState = repository.ongoingCallState
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
new file mode 100644
index 0000000..ad09aa3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.chips.call.ui.viewmodel
+
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.animation.ActivityTransitionAnimator
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** View model for the ongoing phone call chip shown in the status bar. */
+@SysUISingleton
+open class CallChipViewModel
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    interactor: CallChipInteractor,
+    systemClock: SystemClock,
+    private val activityStarter: ActivityStarter,
+) : OngoingActivityChipViewModel {
+    override val chip: StateFlow<OngoingActivityChipModel> =
+        interactor.ongoingCallState
+            .map { state ->
+                when (state) {
+                    is OngoingCallModel.NoCall -> OngoingActivityChipModel.Hidden
+                    is OngoingCallModel.InCall -> {
+                        // This mimics OngoingCallController#updateChip.
+                        // TODO(b/332662551): Handle `state.startTimeMs = 0` correctly (see
+                        // b/192379214 and
+                        // OngoingCallController.CallNotificationInfo.hasValidStartTime).
+                        val startTimeInElapsedRealtime =
+                            state.startTimeMs - systemClock.currentTimeMillis() +
+                                systemClock.elapsedRealtime()
+                        OngoingActivityChipModel.Shown(
+                            icon =
+                                Icon.Resource(
+                                    com.android.internal.R.drawable.ic_phone,
+                                    contentDescription = null,
+                                ),
+                            startTimeMs = startTimeInElapsedRealtime,
+                        ) {
+                            if (state.intent != null) {
+                                val backgroundView =
+                                    it.requireViewById<ChipBackgroundContainer>(
+                                        R.id.ongoing_activity_chip_background
+                                    )
+                                // TODO(b/332662551): Log the click event.
+                                // This mimics OngoingCallController#updateChipClickListener.
+                                activityStarter.postStartActivityDismissingKeyguard(
+                                    state.intent,
+                                    ActivityTransitionAnimator.Controller.fromView(
+                                        backgroundView,
+                                        InteractionJankMonitor
+                                            .CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+                                    )
+                                )
+                            }
+                        }
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastToOtherDeviceDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastToOtherDeviceDialogDelegate.kt
new file mode 100644
index 0000000..bc0f492
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastToOtherDeviceDialogDelegate.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.statusbar.chips.casttootherdevice.ui.view
+
+import android.os.Bundle
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel.Companion.CAST_TO_OTHER_DEVICE_ICON
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** A dialog that lets the user stop an ongoing cast-screen-to-other-device event. */
+class EndCastToOtherDeviceDialogDelegate(
+    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+    private val stopAction: () -> Unit,
+    private val state: ProjectionChipModel.Projecting,
+) : SystemUIDialog.Delegate {
+    override fun createDialog(): SystemUIDialog {
+        return endMediaProjectionDialogHelper.createDialog(this)
+    }
+
+    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        with(dialog) {
+            setIcon(CAST_TO_OTHER_DEVICE_ICON)
+            setTitle(R.string.cast_to_other_device_stop_dialog_title)
+            setMessage(
+                endMediaProjectionDialogHelper.getDialogMessage(
+                    state.projectionState,
+                    genericMessageResId = R.string.cast_to_other_device_stop_dialog_message,
+                    specificAppMessageResId =
+                        R.string.cast_to_other_device_stop_dialog_message_specific_app,
+                )
+            )
+            // No custom on-click, because the dialog will automatically be dismissed when the
+            // button is clicked anyway.
+            setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
+            setPositiveButton(R.string.cast_to_other_device_stop_dialog_button) { _, _ ->
+                stopAction.invoke()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
new file mode 100644
index 0000000..a1678bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.chips.casttootherdevice.ui.viewmodel
+
+import androidx.annotation.DrawableRes
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.casttootherdevice.ui.view.EndCastToOtherDeviceDialogDelegate
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * View model for the cast-to-other-device chip, shown when sharing your phone screen content to a
+ * different device. (Triggered from the Quick Settings Cast tile or from the Settings app.)
+ */
+@SysUISingleton
+class CastToOtherDeviceChipViewModel
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val mediaProjectionChipInteractor: MediaProjectionChipInteractor,
+    private val systemClock: SystemClock,
+    private val dialogTransitionAnimator: DialogTransitionAnimator,
+    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+) : OngoingActivityChipViewModel {
+    override val chip: StateFlow<OngoingActivityChipModel> =
+        // TODO(b/342169876): The MediaProjection APIs are not invoked for certain
+        // cast-to-other-device events, like audio-only casting. We should also listen to
+        // MediaRouter APIs to cover all cast events.
+        mediaProjectionChipInteractor.projection
+            .map { projectionModel ->
+                when (projectionModel) {
+                    is ProjectionChipModel.NotProjecting -> OngoingActivityChipModel.Hidden
+                    is ProjectionChipModel.Projecting -> {
+                        if (projectionModel.type != ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE) {
+                            OngoingActivityChipModel.Hidden
+                        } else {
+                            createCastToOtherDeviceChip(projectionModel)
+                        }
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+
+    /** Stops the currently active projection. */
+    private fun stopProjecting() {
+        mediaProjectionChipInteractor.stopProjecting()
+    }
+
+    private fun createCastToOtherDeviceChip(
+        state: ProjectionChipModel.Projecting,
+    ): OngoingActivityChipModel.Shown {
+        return OngoingActivityChipModel.Shown(
+            icon =
+                Icon.Resource(
+                    CAST_TO_OTHER_DEVICE_ICON,
+                    ContentDescription.Resource(R.string.accessibility_casting),
+                ),
+            // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
+            startTimeMs = systemClock.elapsedRealtime(),
+            createDialogLaunchOnClickListener(
+                createCastToOtherDeviceDialogDelegate(state),
+                dialogTransitionAnimator,
+            ),
+        )
+    }
+
+    private fun createCastToOtherDeviceDialogDelegate(state: ProjectionChipModel.Projecting) =
+        EndCastToOtherDeviceDialogDelegate(
+            endMediaProjectionDialogHelper,
+            stopAction = this::stopProjecting,
+            state,
+        )
+
+    companion object {
+        @DrawableRes val CAST_TO_OTHER_DEVICE_ICON = R.drawable.ic_cast_connected
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt
deleted file mode 100644
index c3d37fb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt
+++ /dev/null
@@ -1,26 +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.statusbar.chips.domain.interactor
-
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import kotlinx.coroutines.flow.StateFlow
-
-/** Interface for an interactor that knows the state of a single type of ongoing activity chip. */
-interface OngoingActivityChipInteractor {
-    /** A flow modeling the chip that should be shown. */
-    val chip: StateFlow<OngoingActivityChipModel>
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
index ac16d26..20ebae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -16,64 +16,69 @@
 
 package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
 
-import com.android.systemui.common.shared.model.ContentDescription
-import com.android.systemui.common.shared.model.Icon
+import android.content.pm.PackageManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import com.android.systemui.util.time.SystemClock
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
+import com.android.systemui.util.Utils
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /**
- * Interactor for media-projection-related chips in the status bar.
- *
- * There are two kinds of media projection events that will show chips in the status bar:
- * 1) Share-to-app: Sharing your phone screen content to another app on the same device. (Triggered
- *    from within each individual app.)
- * 2) Cast-to-other-device: Sharing your phone screen content to a different device. (Triggered from
- *    the Quick Settings Cast tile or from the Settings app.) This interactor handles both of those
- *    event types (though maybe not audio-only casting -- see b/342169876).
+ * Interactor for media projection events, used to show chips in the status bar for share-to-app and
+ * cast-to-other-device events. See
+ * [com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel] and
+ * [com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel]
+ * for more details on what those events are.
  */
 @SysUISingleton
 class MediaProjectionChipInteractor
 @Inject
 constructor(
-    @Application scope: CoroutineScope,
-    mediaProjectionRepository: MediaProjectionRepository,
-    val systemClock: SystemClock,
-) : OngoingActivityChipInteractor {
-    override val chip: StateFlow<OngoingActivityChipModel> =
+    @Application private val scope: CoroutineScope,
+    private val mediaProjectionRepository: MediaProjectionRepository,
+    private val packageManager: PackageManager,
+) {
+    val projection: StateFlow<ProjectionChipModel> =
         mediaProjectionRepository.mediaProjectionState
             .map { state ->
                 when (state) {
-                    is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden
-                    is MediaProjectionState.EntireScreen,
-                    is MediaProjectionState.SingleTask -> {
-                        // TODO(b/332662551): Distinguish between cast-to-other-device and
-                        // share-to-app.
-                        OngoingActivityChipModel.Shown(
-                            icon =
-                                Icon.Resource(
-                                    R.drawable.ic_cast_connected,
-                                    ContentDescription.Resource(R.string.accessibility_casting)
-                                ),
-                            // TODO(b/332662551): See if we can use a MediaProjection API to fetch
-                            // this time.
-                            startTimeMs = systemClock.elapsedRealtime()
-                        ) {
-                            // TODO(b/332662551): Implement the pause dialog.
-                        }
+                    is MediaProjectionState.NotProjecting -> ProjectionChipModel.NotProjecting
+                    is MediaProjectionState.Projecting -> {
+                        val type =
+                            if (isProjectionToOtherDevice(state.hostPackage)) {
+                                ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE
+                            } else {
+                                ProjectionChipModel.Type.SHARE_TO_APP
+                            }
+                        ProjectionChipModel.Projecting(type, state)
                     }
                 }
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), ProjectionChipModel.NotProjecting)
+
+    /** Stops the currently active projection. */
+    fun stopProjecting() {
+        scope.launch { mediaProjectionRepository.stopProjecting() }
+    }
+
+    /**
+     * Returns true iff projecting to the given [packageName] means that we're projecting to a
+     * *different* device (as opposed to projecting to some application on *this* device).
+     */
+    private fun isProjectionToOtherDevice(packageName: String?): Boolean {
+        // The [isHeadlessRemoteDisplayProvider] check approximates whether a projection is to a
+        // different device or the same device, because headless remote display packages are the
+        // only kinds of packages that do cast-to-other-device. This isn't exactly perfect,
+        // because it means that any projection by those headless remote display packages will be
+        // marked as going to a different device, even if that isn't always true. See b/321078669.
+        return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/ProjectionChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/ProjectionChipModel.kt
new file mode 100644
index 0000000..85682f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/model/ProjectionChipModel.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.chips.mediaprojection.domain.model
+
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+
+/**
+ * Represents the state of media projection needed to show chips in the status bar. In particular,
+ * also includes what type of projection is occurring.
+ */
+sealed class ProjectionChipModel {
+    /** There is no media being projected. */
+    data object NotProjecting : ProjectionChipModel()
+
+    /** Media is currently being projected. */
+    data class Projecting(
+        val type: Type,
+        val projectionState: MediaProjectionState.Projecting,
+    ) : ProjectionChipModel()
+
+    enum class Type {
+        /**
+         * This projection is sharing your phone screen content to another app on the same device.
+         */
+        SHARE_TO_APP,
+        /** This projection is sharing your phone screen content to a different device. */
+        CAST_TO_OTHER_DEVICE,
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
new file mode 100644
index 0000000..402306a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.ui.view
+
+import android.annotation.StringRes
+import android.app.ActivityManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.text.Html
+import android.text.Html.FROM_HTML_MODE_LEGACY
+import android.text.TextUtils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import javax.inject.Inject
+
+/** Helper class for showing dialogs that let users end different types of media projections. */
+@SysUISingleton
+class EndMediaProjectionDialogHelper
+@Inject
+constructor(
+    private val dialogFactory: SystemUIDialog.Factory,
+    private val packageManager: PackageManager,
+    private val context: Context
+) {
+    /** Creates a new [SystemUIDialog] using the given delegate. */
+    fun createDialog(delegate: SystemUIDialog.Delegate): SystemUIDialog {
+        return dialogFactory.create(delegate)
+    }
+
+    /** See other [getDialogMessage]. */
+    fun getDialogMessage(
+        state: MediaProjectionState.Projecting,
+        @StringRes genericMessageResId: Int,
+        @StringRes specificAppMessageResId: Int,
+    ): CharSequence {
+        val specificTaskInfo =
+            if (state is MediaProjectionState.Projecting.SingleTask) {
+                state.task
+            } else {
+                null
+            }
+        return getDialogMessage(specificTaskInfo, genericMessageResId, specificAppMessageResId)
+    }
+
+    /**
+     * Returns the message to show in the dialog based on the specific media projection state.
+     *
+     * @param genericMessageResId a res ID for a more generic "end projection" message
+     * @param specificAppMessageResId a res ID for an "end projection" message that also lets us
+     *   specify which app is currently being projected.
+     */
+    fun getDialogMessage(
+        specificTaskInfo: ActivityManager.RunningTaskInfo?,
+        @StringRes genericMessageResId: Int,
+        @StringRes specificAppMessageResId: Int,
+    ): CharSequence {
+        if (specificTaskInfo == null) {
+            return context.getString(genericMessageResId)
+        }
+        val packageName =
+            specificTaskInfo.baseIntent.component?.packageName
+                ?: return context.getString(genericMessageResId)
+        return try {
+            val appInfo = packageManager.getApplicationInfo(packageName, 0)
+            val appName = appInfo.loadLabel(packageManager)
+            getSpecificAppMessageText(specificAppMessageResId, appName)
+        } catch (e: PackageManager.NameNotFoundException) {
+            // TODO(b/332662551): Log this error.
+            context.getString(genericMessageResId)
+        }
+    }
+
+    private fun getSpecificAppMessageText(
+        @StringRes specificAppMessageResId: Int,
+        appName: CharSequence,
+    ): CharSequence {
+        // https://developer.android.com/guide/topics/resources/string-resource#StylingWithHTML
+        val escapedAppName = TextUtils.htmlEncode(appName.toString())
+        val text = context.getString(specificAppMessageResId, escapedAppName)
+        return Html.fromHtml(text, FROM_HTML_MODE_LEGACY)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 585ff5f..1e9f0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -16,51 +16,60 @@
 
 package com.android.systemui.statusbar.chips.screenrecord.domain.interactor
 
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.res.R
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
-import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import com.android.systemui.util.time.SystemClock
+import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /** Interactor for the screen recording chip shown in the status bar. */
 @SysUISingleton
 class ScreenRecordChipInteractor
 @Inject
 constructor(
-    @Application scope: CoroutineScope,
-    screenRecordRepository: ScreenRecordRepository,
-    val systemClock: SystemClock,
-) : OngoingActivityChipInteractor {
-    override val chip: StateFlow<OngoingActivityChipModel> =
-        screenRecordRepository.screenRecordState
-            .map { state ->
-                when (state) {
-                    is ScreenRecordModel.DoingNothing,
+    @Application private val scope: CoroutineScope,
+    private val screenRecordRepository: ScreenRecordRepository,
+    private val mediaProjectionRepository: MediaProjectionRepository,
+) {
+    val screenRecordState: StateFlow<ScreenRecordChipModel> =
+        // ScreenRecordRepository has the main "is the screen being recorded?" state, and
+        // MediaProjectionRepository has information about what specifically is being recorded (a
+        // single app or the entire screen)
+        combine(
+                screenRecordRepository.screenRecordState,
+                mediaProjectionRepository.mediaProjectionState,
+            ) { screenRecordState, mediaProjectionState ->
+                when (screenRecordState) {
+                    is ScreenRecordModel.DoingNothing -> ScreenRecordChipModel.DoingNothing
                     // TODO(b/332662551): Implement the 3-2-1 countdown chip.
-                    is ScreenRecordModel.Starting -> OngoingActivityChipModel.Hidden
-                    is ScreenRecordModel.Recording ->
-                        OngoingActivityChipModel.Shown(
-                            // TODO(b/332662551): Also provide a content description.
-                            icon =
-                                Icon.Resource(
-                                    R.drawable.stat_sys_screen_record,
-                                    contentDescription = null
-                                ),
-                            startTimeMs = systemClock.elapsedRealtime()
-                        ) {
-                            // TODO(b/332662551): Implement the pause dialog.
-                        }
+                    is ScreenRecordModel.Starting ->
+                        ScreenRecordChipModel.Starting(screenRecordState.millisUntilStarted)
+                    is ScreenRecordModel.Recording -> {
+                        val recordedTask =
+                            if (
+                                mediaProjectionState is MediaProjectionState.Projecting.SingleTask
+                            ) {
+                                mediaProjectionState.task
+                            } else {
+                                null
+                            }
+                        ScreenRecordChipModel.Recording(recordedTask)
+                    }
                 }
             }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), ScreenRecordChipModel.DoingNothing)
+
+    /** Stops the recording. */
+    fun stopRecording() {
+        scope.launch { screenRecordRepository.stopRecording() }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt
new file mode 100644
index 0000000..ba6cd4d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.chips.screenrecord.domain.model
+
+import android.app.ActivityManager
+
+/** Represents the status of screen record needed to show a chip in the status bar. */
+sealed interface ScreenRecordChipModel {
+    /** There's nothing related to screen recording happening. */
+    data object DoingNothing : ScreenRecordChipModel
+
+    /** A screen recording will begin in [millisUntilStarted] ms. */
+    data class Starting(val millisUntilStarted: Long) : ScreenRecordChipModel
+
+    /**
+     * There's an active screen recording happening.
+     *
+     * @property recordedTask the task being recorded if the user is recording only a single app.
+     *   Null if the user is recording the entire screen or we don't have the task info yet.
+     */
+    data class Recording(
+        val recordedTask: ActivityManager.RunningTaskInfo?,
+    ) : ScreenRecordChipModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt
new file mode 100644
index 0000000..9adbff9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.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.statusbar.chips.screenrecord.ui.view
+
+import android.app.ActivityManager
+import android.os.Bundle
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** A dialog that lets the user stop an ongoing screen recording. */
+class EndScreenRecordingDialogDelegate(
+    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+    private val stopAction: () -> Unit,
+    private val recordedTask: ActivityManager.RunningTaskInfo?,
+) : SystemUIDialog.Delegate {
+
+    override fun createDialog(): SystemUIDialog {
+        return endMediaProjectionDialogHelper.createDialog(this)
+    }
+
+    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        with(dialog) {
+            setIcon(ScreenRecordChipViewModel.ICON)
+            setTitle(R.string.screenrecord_stop_dialog_title)
+            setMessage(
+                endMediaProjectionDialogHelper.getDialogMessage(
+                    recordedTask,
+                    genericMessageResId = R.string.screenrecord_stop_dialog_message,
+                    specificAppMessageResId = R.string.screenrecord_stop_dialog_message_specific_app
+                )
+            )
+            // No custom on-click, because the dialog will automatically be dismissed when the
+            // button is clicked anyway.
+            setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
+            setPositiveButton(R.string.screenrecord_stop_dialog_button) { _, _ ->
+                stopAction.invoke()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
new file mode 100644
index 0000000..d190cfd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel
+
+import android.app.ActivityManager
+import androidx.annotation.DrawableRes
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
+import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
+import com.android.systemui.statusbar.chips.screenrecord.ui.view.EndScreenRecordingDialogDelegate
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/** View model for the screen recording chip shown in the status bar. */
+@SysUISingleton
+class ScreenRecordChipViewModel
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val interactor: ScreenRecordChipInteractor,
+    private val systemClock: SystemClock,
+    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+    private val dialogTransitionAnimator: DialogTransitionAnimator,
+) : OngoingActivityChipViewModel {
+    override val chip: StateFlow<OngoingActivityChipModel> =
+        interactor.screenRecordState
+            .map { state ->
+                when (state) {
+                    is ScreenRecordChipModel.DoingNothing -> OngoingActivityChipModel.Hidden
+                    // TODO(b/332662551): Implement the 3-2-1 countdown chip.
+                    is ScreenRecordChipModel.Starting -> OngoingActivityChipModel.Hidden
+                    is ScreenRecordChipModel.Recording -> {
+                        OngoingActivityChipModel.Shown(
+                            // TODO(b/332662551): Also provide a content description.
+                            icon = Icon.Resource(ICON, contentDescription = null),
+                            startTimeMs = systemClock.elapsedRealtime(),
+                            createDialogLaunchOnClickListener(
+                                createDelegate(state.recordedTask),
+                                dialogTransitionAnimator,
+                            ),
+                        )
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+
+    private fun createDelegate(
+        recordedTask: ActivityManager.RunningTaskInfo?
+    ): EndScreenRecordingDialogDelegate {
+        return EndScreenRecordingDialogDelegate(
+            endMediaProjectionDialogHelper,
+            stopAction = interactor::stopRecording,
+            recordedTask,
+        )
+    }
+
+    companion object {
+        @DrawableRes val ICON = R.drawable.ic_screenrecord
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegate.kt
new file mode 100644
index 0000000..7e7ef40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegate.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.chips.sharetoapp.ui.view
+
+import android.os.Bundle
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel.Companion.SHARE_TO_APP_ICON
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+/** A dialog that lets the user stop an ongoing share-screen-to-app event. */
+class EndShareToAppDialogDelegate(
+    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+    private val stopAction: () -> Unit,
+    private val state: ProjectionChipModel.Projecting,
+) : SystemUIDialog.Delegate {
+    override fun createDialog(): SystemUIDialog {
+        return endMediaProjectionDialogHelper.createDialog(this)
+    }
+
+    override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+        with(dialog) {
+            setIcon(SHARE_TO_APP_ICON)
+            setTitle(R.string.share_to_app_stop_dialog_title)
+            setMessage(
+                endMediaProjectionDialogHelper.getDialogMessage(
+                    state.projectionState,
+                    genericMessageResId = R.string.share_to_app_stop_dialog_message,
+                    specificAppMessageResId = R.string.share_to_app_stop_dialog_message_specific_app
+                )
+            )
+            // No custom on-click, because the dialog will automatically be dismissed when the
+            // button is clicked anyway.
+            setNegativeButton(R.string.close_dialog_button, /* onClick= */ null)
+            setPositiveButton(R.string.share_to_app_stop_dialog_button) { _, _ ->
+                stopAction.invoke()
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
new file mode 100644
index 0000000..dc41002
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel
+
+import androidx.annotation.DrawableRes
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * View model for the share-to-app chip, shown when sharing your phone screen content to another app
+ * on the same device. (Triggered from within each individual app.)
+ */
+@SysUISingleton
+class ShareToAppChipViewModel
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    private val mediaProjectionChipInteractor: MediaProjectionChipInteractor,
+    private val systemClock: SystemClock,
+    private val dialogTransitionAnimator: DialogTransitionAnimator,
+    private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
+) : OngoingActivityChipViewModel {
+    override val chip: StateFlow<OngoingActivityChipModel> =
+        mediaProjectionChipInteractor.projection
+            .map { projectionModel ->
+                when (projectionModel) {
+                    is ProjectionChipModel.NotProjecting -> OngoingActivityChipModel.Hidden
+                    is ProjectionChipModel.Projecting -> {
+                        if (projectionModel.type != ProjectionChipModel.Type.SHARE_TO_APP) {
+                            OngoingActivityChipModel.Hidden
+                        } else {
+                            createShareToAppChip(projectionModel)
+                        }
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+
+    /** Stops the currently active projection. */
+    private fun stopProjecting() {
+        mediaProjectionChipInteractor.stopProjecting()
+    }
+
+    private fun createShareToAppChip(
+        state: ProjectionChipModel.Projecting,
+    ): OngoingActivityChipModel.Shown {
+        return OngoingActivityChipModel.Shown(
+            // TODO(b/332662551): Use the right content description.
+            icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null),
+            // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time.
+            startTimeMs = systemClock.elapsedRealtime(),
+            createDialogLaunchOnClickListener(
+                createShareToAppDialogDelegate(state),
+                dialogTransitionAnimator
+            ),
+        )
+    }
+
+    private fun createShareToAppDialogDelegate(state: ProjectionChipModel.Projecting) =
+        EndShareToAppDialogDelegate(
+            endMediaProjectionDialogHelper,
+            stopAction = this::stopProjecting,
+            state,
+        )
+
+    companion object {
+        // TODO(b/332662551): Use the right icon.
+        @DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_screenshot_share
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/model/OngoingActivityChipModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index c753918..e63713b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.chips.domain.model
+package com.android.systemui.statusbar.chips.ui.model
 
 import android.view.View
 import com.android.systemui.common.shared.model.Icon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt
new file mode 100644
index 0000000..0dbf5d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.chips.ui.viewmodel
+
+import android.view.View
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface for a view model that knows the display requirements for a single type of ongoing
+ * activity chip.
+ */
+interface OngoingActivityChipViewModel {
+    /** A flow modeling the chip that should be shown (or not shown). */
+    val chip: StateFlow<OngoingActivityChipModel>
+
+    companion object {
+        /** Creates a chip click listener that launches a dialog created by [dialogDelegate]. */
+        fun createDialogLaunchOnClickListener(
+            dialogDelegate: SystemUIDialog.Delegate,
+            dialogTransitionAnimator: DialogTransitionAnimator,
+        ): View.OnClickListener {
+            return View.OnClickListener { view ->
+                val dialog = dialogDelegate.createDialog()
+                val launchableView =
+                    view.requireViewById<ChipBackgroundContainer>(
+                        R.id.ongoing_activity_chip_background
+                    )
+                // TODO(b/343699052): This makes a beautiful animate-in, but the
+                //  animate-out looks odd because the dialog animates back into the chip
+                //  but then the chip disappears. If we aren't able to address
+                //  b/343699052 in time for launch, we should just use `dialog.show`.
+                dialogTransitionAnimator.showFromView(dialog, launchableView)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index edb2983..1b79ce4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -18,10 +18,11 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
-import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
+import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
+import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -40,11 +41,11 @@
 @Inject
 constructor(
     @Application scope: CoroutineScope,
-    screenRecordChipInteractor: ScreenRecordChipInteractor,
-    mediaProjectionChipInteractor: MediaProjectionChipInteractor,
-    callChipInteractor: CallChipInteractor,
+    screenRecordChipViewModel: ScreenRecordChipViewModel,
+    shareToAppChipViewModel: ShareToAppChipViewModel,
+    castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
+    callChipViewModel: CallChipViewModel,
 ) {
-
     /**
      * A flow modeling the chip that should be shown in the status bar after accounting for possibly
      * multiple ongoing activities.
@@ -54,10 +55,11 @@
      */
     val chip: StateFlow<OngoingActivityChipModel> =
         combine(
-                screenRecordChipInteractor.chip,
-                mediaProjectionChipInteractor.chip,
-                callChipInteractor.chip
-            ) { screenRecord, mediaProjection, call ->
+                screenRecordChipViewModel.chip,
+                shareToAppChipViewModel.chip,
+                castToOtherDeviceChipViewModel.chip,
+                callChipViewModel.chip,
+            ) { screenRecord, shareToApp, castToOtherDevice, call ->
                 // This `when` statement shows the priority order of the chips
                 when {
                     // Screen recording also activates the media projection APIs, so whenever the
@@ -65,7 +67,8 @@
                     // active. We want the screen-recording-specific chip shown in this case, so we
                     // give the screen recording chip priority. See b/296461748.
                     screenRecord is OngoingActivityChipModel.Shown -> screenRecord
-                    mediaProjection is OngoingActivityChipModel.Shown -> mediaProjection
+                    shareToApp is OngoingActivityChipModel.Shown -> shareToApp
+                    castToOtherDevice is OngoingActivityChipModel.Shown -> castToOtherDevice
                     else -> call
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index c7952b6..400f8af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.qs.tiles.InternetTile
 import com.android.systemui.qs.tiles.InternetTileNewImpl
 import com.android.systemui.qs.tiles.NfcTile
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper
 import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileDataInteractor
@@ -90,6 +91,27 @@
     /** Inject NfcTile into tileMap in QSModule */
     @Binds @IntoMap @StringKey(NfcTile.TILE_SPEC) fun bindNfcTile(nfcTile: NfcTile): QSTileImpl<*>
 
+    @Binds
+    @IntoMap
+    @StringKey(AIRPLANE_MODE_TILE_SPEC)
+    fun provideAirplaneModeAvailabilityInteractor(
+            impl: AirplaneModeTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(DATA_SAVER_TILE_SPEC)
+    fun provideDataSaverAvailabilityInteractor(
+            impl: DataSaverTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(INTERNET_TILE_SPEC)
+    fun provideInternetAvailabilityInteractor(
+            impl: InternetTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
     companion object {
 
         const val AIRPLANE_MODE_TILE_SPEC = "airplane"
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 27536bc..c8f0b52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.dagger;
 
+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.StatusBarNotificationPresenterModule;
@@ -26,6 +27,6 @@
 /**  */
 @Module(includes = {CentralSurfacesDependenciesModule.class,
         StatusBarNotificationPresenterModule.class, StatusBarPhoneModule.class,
-        NotificationsModule.class, NotificationRowModule.class})
+        NotificationsModule.class, NotificationRowModule.class, EmergencyGestureModule.class})
 public interface CentralSurfacesModule {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 11636bd..088c86d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -224,8 +225,8 @@
                 modifiedStatusBarAttributes,
                 isTransientShown,
                 isInFullscreenMode,
-                ongoingCallRepository.hasOngoingCall,
-            ) { modifiedAttributes, isTransientShown, isInFullscreenMode, hasOngoingCall ->
+                ongoingCallRepository.ongoingCallState,
+            ) { modifiedAttributes, isTransientShown, isInFullscreenMode, ongoingCallState ->
                 if (modifiedAttributes == null) {
                     null
                 } else {
@@ -234,7 +235,7 @@
                             modifiedAttributes.appearance,
                             isTransientShown,
                             isInFullscreenMode,
-                            hasOngoingCall,
+                            hasOngoingCall = ongoingCallState is OngoingCallModel.InCall,
                         )
                     StatusBarAppearance(
                         statusBarMode,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 2e87a5b..af8a89d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -38,6 +38,7 @@
 import android.view.ViewGroup
 import androidx.annotation.VisibleForTesting
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.Utils
 import com.android.systemui.Dumpable
 import com.android.systemui.Flags.smartspaceLockscreenViewmodel
@@ -53,6 +54,7 @@
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.TimeChangedDelegate
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -107,6 +109,7 @@
         @Main private val uiExecutor: Executor,
         @Background private val bgExecutor: Executor,
         @Main private val handler: Handler,
+        @Background private val bgHandler: Handler,
         @Named(DATE_SMARTSPACE_DATA_PLUGIN)
         optionalDatePlugin: Optional<BcSmartspaceDataPlugin>,
         @Named(WEATHER_SMARTSPACE_DATA_PLUGIN)
@@ -410,7 +413,9 @@
 
         val ssView = plugin.getView(parent)
         configPlugin?.let { ssView.registerConfigProvider(it) }
+        ssView.setBgHandler(bgHandler)
         ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+        ssView.setTimeChangedDelegate(SmartspaceTimeChangedDelegate(keyguardUpdateMonitor))
         ssView.registerDataProvider(plugin)
 
         ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter {
@@ -682,5 +687,28 @@
             }
         }
     }
+
+    private class SmartspaceTimeChangedDelegate(
+        private val keyguardUpdateMonitor: KeyguardUpdateMonitor
+    ) : TimeChangedDelegate {
+        private var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback? = null
+        override fun register(callback: Runnable) {
+            if (keyguardUpdateMonitorCallback != null) {
+                unregister()
+            }
+            keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
+                override fun onTimeChanged() {
+                    callback.run()
+                }
+            }
+            keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+            callback.run()
+        }
+
+        override fun unregister() {
+            keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
+            keyguardUpdateMonitorCallback = null
+        }
+    }
 }
 
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 2f293e0..f62b24a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -18,33 +18,29 @@
 
 import android.content.Context
 import android.provider.DeviceConfig
-
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 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
 import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
 import com.android.systemui.statusbar.notification.stack.BUCKET_MEDIA_CONTROLS
 import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
-import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
+import com.android.systemui.statusbar.notification.stack.PriorityBucket
 import com.android.systemui.util.DeviceConfigProxy
 import com.android.systemui.util.Utils
-
 import javax.inject.Inject
 
 private var sUsePeopleFiltering: Boolean? = null
 
-/**
- * Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config.
- */
+/** Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config. */
 @SysUISingleton
-class NotificationSectionsFeatureManager @Inject constructor(
-    val proxy: DeviceConfigProxy,
-    val context: Context
-) {
+class NotificationSectionsFeatureManager
+@Inject
+constructor(val proxy: DeviceConfigProxy, val context: Context) {
 
     fun isFilteringEnabled(): Boolean {
         return usePeopleFiltering(proxy)
@@ -55,30 +51,37 @@
     }
 
     fun getNotificationBuckets(): IntArray {
-        if (PriorityPeopleSection.isEnabled) {
+        if (PriorityPeopleSection.isEnabled || NotificationMinimalismPrototype.V2.isEnabled) {
             // We don't need this list to be adaptive, it can be the superset of all features.
-            return intArrayOf(
-                    BUCKET_MEDIA_CONTROLS,
-                    BUCKET_HEADS_UP,
-                    BUCKET_FOREGROUND_SERVICE,
-                    BUCKET_PRIORITY_PEOPLE,
-                    BUCKET_PEOPLE,
-                    BUCKET_ALERTING,
-                    BUCKET_SILENT,
-                )
+            return PriorityBucket.getAllInOrder()
         }
         return when {
             isFilteringEnabled() && isMediaControlsEnabled() ->
-                intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS,
-                        BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
+                intArrayOf(
+                    BUCKET_HEADS_UP,
+                    BUCKET_FOREGROUND_SERVICE,
+                    BUCKET_MEDIA_CONTROLS,
+                    BUCKET_PEOPLE,
+                    BUCKET_ALERTING,
+                    BUCKET_SILENT
+                )
             !isFilteringEnabled() && isMediaControlsEnabled() ->
-                intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS,
-                        BUCKET_ALERTING, BUCKET_SILENT)
+                intArrayOf(
+                    BUCKET_HEADS_UP,
+                    BUCKET_FOREGROUND_SERVICE,
+                    BUCKET_MEDIA_CONTROLS,
+                    BUCKET_ALERTING,
+                    BUCKET_SILENT
+                )
             isFilteringEnabled() && !isMediaControlsEnabled() ->
-                intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_PEOPLE,
-                        BUCKET_ALERTING, BUCKET_SILENT)
-            else ->
-                intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
+                intArrayOf(
+                    BUCKET_HEADS_UP,
+                    BUCKET_FOREGROUND_SERVICE,
+                    BUCKET_PEOPLE,
+                    BUCKET_ALERTING,
+                    BUCKET_SILENT
+                )
+            else -> intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
         }
     }
 
@@ -94,8 +97,12 @@
 
 private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean {
     if (sUsePeopleFiltering == null) {
-        sUsePeopleFiltering = proxy.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, true)
+        sUsePeopleFiltering =
+            proxy.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                NOTIFICATIONS_USE_PEOPLE_FILTERING,
+                true
+            )
     }
 
     return sUsePeopleFiltering!!
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index b397906..1adfef0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -68,6 +68,9 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel;
+import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel;
+import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 import com.android.systemui.statusbar.notification.stack.PriorityBucket;
 import com.android.systemui.util.ListenerSet;
 
@@ -951,6 +954,7 @@
      * heads up.
      */
     public void setHeadsUpStatusBarText(CharSequence headsUpStatusBarText) {
+        NotificationRowContentBinderRefactor.assertInLegacyMode();
         this.mHeadsUpStatusBarText.setValue(headsUpStatusBarText);
     }
 
@@ -964,6 +968,7 @@
      * heads up, and its content is sensitive right now.
      */
     public void setHeadsUpStatusBarTextPublic(CharSequence headsUpStatusBarTextPublic) {
+        NotificationRowContentBinderRefactor.assertInLegacyMode();
         this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarTextPublic);
     }
 
@@ -1036,6 +1041,14 @@
                 == Notification.VISIBILITY_PRIVATE;
     }
 
+    /** Set the content generated by the notification inflater. */
+    public void setContentModel(NotificationContentModel contentModel) {
+        if (NotificationRowContentBinderRefactor.isUnexpectedlyInLegacyMode()) return;
+        HeadsUpStatusBarModel headsUpStatusBarModel = contentModel.getHeadsUpStatusBarModel();
+        this.mHeadsUpStatusBarText.setValue(headsUpStatusBarModel.getPrivateText());
+        this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText());
+    }
+
     /** Information about a suggestion that is being edited. */
     public static class EditedSuggestionInfo {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
index 63997f8..47a0429 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
@@ -60,22 +60,27 @@
         public boolean isInSection(ListEntry entry) {
             NotificationEntry notificationEntry = entry.getRepresentativeEntry();
             if (notificationEntry != null) {
-                return isColorizedForegroundService(notificationEntry) || isCall(notificationEntry);
+                return isRichOngoing(notificationEntry);
             }
             return false;
         }
-
-        private boolean isColorizedForegroundService(NotificationEntry entry) {
-            Notification notification = entry.getSbn().getNotification();
-            return notification.isForegroundService()
-                    && notification.isColorized()
-                    && entry.getImportance() > IMPORTANCE_MIN;
-        }
-
-        private boolean isCall(NotificationEntry entry) {
-            Notification notification = entry.getSbn().getNotification();
-            return entry.getImportance() > IMPORTANCE_MIN
-                    && notification.isStyle(Notification.CallStyle.class);
-        }
     };
+
+    /** Determines if the given notification is a colorized or call notification */
+    public static boolean isRichOngoing(NotificationEntry entry) {
+        return isColorizedForegroundService(entry) || isCall(entry);
+    }
+
+    private static boolean isColorizedForegroundService(NotificationEntry entry) {
+        Notification notification = entry.getSbn().getNotification();
+        return notification.isForegroundService()
+                && notification.isColorized()
+                && entry.getImportance() > IMPORTANCE_MIN;
+    }
+
+    private static boolean isCall(NotificationEntry entry) {
+        Notification notification = entry.getSbn().getNotification();
+        return entry.getImportance() > IMPORTANCE_MIN
+                && notification.isStyle(Notification.CallStyle.class);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 071192b..5a1146d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator
 
+import android.app.NotificationManager
 import android.os.UserHandle
 import android.provider.Settings
 import androidx.annotation.VisibleForTesting
@@ -44,7 +45,8 @@
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
 import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
-import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
+import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_ONGOING
+import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_UNSEEN
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.headsUpEvents
 import com.android.systemui.util.asIndenting
@@ -113,7 +115,7 @@
     private fun attachUnseenFilter(pipeline: NotifPipeline) {
         if (NotificationMinimalismPrototype.V2.isEnabled) {
             pipeline.addPromoter(unseenNotifPromoter)
-            pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotif)
+            pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotifs)
         }
         pipeline.addFinalizeFilter(unseenNotifFilter)
         pipeline.addCollectionListener(collectionListener)
@@ -347,15 +349,16 @@
             }
         }
 
-    private fun pickOutTopUnseenNotif(list: List<ListEntry>) {
+    private fun pickOutTopUnseenNotifs(list: List<ListEntry>) {
         if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
         // Only ever elevate a top unseen notification on keyguard, not even locked shade
         if (statusBarStateController.state != StatusBarState.KEYGUARD) {
+            seenNotificationsInteractor.setTopOngoingNotification(null)
             seenNotificationsInteractor.setTopUnseenNotification(null)
             return
         }
         // On keyguard pick the top-ranked unseen or ongoing notification to elevate
-        seenNotificationsInteractor.setTopUnseenNotification(
+        val nonSummaryEntries: Sequence<NotificationEntry> =
             list
                 .asSequence()
                 .flatMap {
@@ -365,7 +368,15 @@
                         else -> error("unhandled type of $it")
                     }
                 }
-                .filter { shouldIgnoreUnseenCheck(it) || it in unseenNotifications }
+                .filter { it.importance >= NotificationManager.IMPORTANCE_DEFAULT }
+        seenNotificationsInteractor.setTopOngoingNotification(
+            nonSummaryEntries
+                .filter { ColorizedFgsCoordinator.isRichOngoing(it) }
+                .minByOrNull { it.ranking.rank }
+        )
+        seenNotificationsInteractor.setTopUnseenNotification(
+            nonSummaryEntries
+                .filter { !ColorizedFgsCoordinator.isRichOngoing(it) && it in unseenNotifications }
                 .minByOrNull { it.ranking.rank }
         )
     }
@@ -375,29 +386,39 @@
         object : NotifPromoter("$TAG-unseen") {
             override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean =
                 if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
+                else if (!NotificationMinimalismPrototype.V2.ungroupTopUnseen) false
                 else
-                    seenNotificationsInteractor.isTopUnseenNotification(child) &&
-                        NotificationMinimalismPrototype.V2.ungroupTopUnseen
+                    seenNotificationsInteractor.isTopOngoingNotification(child) ||
+                        seenNotificationsInteractor.isTopUnseenNotification(child)
         }
 
-    val unseenNotifSectioner =
-        object : NotifSectioner("Unseen", BUCKET_FOREGROUND_SERVICE) {
+    val topOngoingSectioner =
+        object : NotifSectioner("TopOngoing", BUCKET_TOP_ONGOING) {
             override fun isInSection(entry: ListEntry): Boolean {
                 if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false
-                if (
-                    seenNotificationsInteractor.isTopUnseenNotification(entry.representativeEntry)
-                ) {
-                    return true
-                }
-                if (entry !is GroupEntry) {
-                    return false
-                }
-                return entry.children.any {
-                    seenNotificationsInteractor.isTopUnseenNotification(it)
+                return entry.anyEntry { notificationEntry ->
+                    seenNotificationsInteractor.isTopOngoingNotification(notificationEntry)
                 }
             }
         }
 
+    val topUnseenSectioner =
+        object : NotifSectioner("TopUnseen", BUCKET_TOP_UNSEEN) {
+            override fun isInSection(entry: ListEntry): Boolean {
+                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false
+                return entry.anyEntry { notificationEntry ->
+                    seenNotificationsInteractor.isTopUnseenNotification(notificationEntry)
+                }
+            }
+        }
+
+    private fun ListEntry.anyEntry(predicate: (NotificationEntry?) -> Boolean) =
+        when {
+            predicate(representativeEntry) -> true
+            this !is GroupEntry -> false
+            else -> children.any(predicate)
+        }
+
     @VisibleForTesting
     internal val unseenNotifFilter =
         object : NotifFilter("$TAG-unseen") {
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 4506385..e413522 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
@@ -116,11 +116,14 @@
         }
 
         // Manually add Ordered Sections
-        mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
-        mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
         if (NotificationMinimalismPrototype.V2.isEnabled) {
-            mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS)
+            mOrderedSections.add(keyguardCoordinator.topOngoingSectioner) // Top Ongoing
         }
+        mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
+        if (NotificationMinimalismPrototype.V2.isEnabled) {
+            mOrderedSections.add(keyguardCoordinator.topUnseenSectioner) // Top Unseen
+        }
+        mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
         if (PriorityPeopleSection.isEnabled) {
             mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index e2c9e02..45d1034 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -42,6 +42,9 @@
     /** Stats about the list of notifications attached to the shade */
     val notifStats = MutableStateFlow(NotifStats.empty)
 
+    /** The key of the top ongoing notification */
+    val topOngoingNotificationKey = MutableStateFlow<String?>(null)
+
     /** The key of the top unseen notification */
     val topUnseenNotificationKey = MutableStateFlow<String?>(null)
 }
@@ -75,6 +78,7 @@
     /** Unique key identifying an [ActiveNotificationEntryModel] in the store. */
     sealed class Key {
         data class Individual(val key: String) : Key()
+
         data class Group(val key: String) : Key()
     }
 
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 42828d9..85c66bd 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
@@ -39,6 +39,18 @@
         notificationListRepository.hasFilteredOutSeenNotifications.value = value
     }
 
+    /** Set the entry that is identified as the top ongoing notification. */
+    fun setTopOngoingNotification(entry: NotificationEntry?) {
+        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
+        notificationListRepository.topOngoingNotificationKey.value = entry?.key
+    }
+
+    /** Determine if the given notification is the top ongoing notification. */
+    fun isTopOngoingNotification(entry: NotificationEntry?): Boolean =
+        if (NotificationMinimalismPrototype.V2.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.V2.isUnexpectedlyInLegacyMode()) return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 3df9374..331d3cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -20,8 +20,6 @@
 import android.app.Notification.MessagingStyle
 import android.app.Person
 import android.content.pm.LauncherApps
-import android.graphics.drawable.AdaptiveIconDrawable
-import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.os.Build
 import android.os.Bundle
@@ -226,28 +224,22 @@
         }
 
         val n = entry.sbn.notification
-        var usingMonochromeAppIcon = false
-        val icon: Icon?
-        if (showPeopleAvatar) {
-            icon = createPeopleAvatar(entry)
-        } else if (android.app.Flags.notificationsUseMonochromeAppIcon()) {
-            if (n.shouldUseAppIcon()) {
-                icon =
-                    getMonochromeAppIcon(entry)?.also { usingMonochromeAppIcon = true }
-                        ?: n.smallIcon
+        val (icon: Icon?, type: StatusBarIcon.Type) =
+            if (showPeopleAvatar) {
+                createPeopleAvatar(entry) to StatusBarIcon.Type.PeopleAvatar
+            } else if (
+                android.app.Flags.notificationsUseMonochromeAppIcon() && n.shouldUseAppIcon()
+            ) {
+                n.smallIcon to StatusBarIcon.Type.MaybeMonochromeAppIcon
             } else {
-                icon = n.smallIcon
+                n.smallIcon to StatusBarIcon.Type.NotifSmallIcon
             }
-        } else {
-            icon = n.smallIcon
-        }
-
         if (icon == null) {
             throw InflationException("No icon in notification from ${entry.sbn.packageName}")
         }
 
-        val sbi = icon.toStatusBarIcon(entry)
-        cacheIconDescriptor(entry, sbi, showPeopleAvatar, usingMonochromeAppIcon)
+        val sbi = icon.toStatusBarIcon(entry, type)
+        cacheIconDescriptor(entry, sbi)
         return sbi
     }
 
@@ -269,29 +261,24 @@
         }
     }
 
-    private fun cacheIconDescriptor(
-        entry: NotificationEntry,
-        descriptor: StatusBarIcon,
-        showPeopleAvatar: Boolean,
-        usingMonochromeAppIcon: Boolean
-    ) {
-        if (android.app.Flags.notificationsUseAppIcon() ||
-            android.app.Flags.notificationsUseMonochromeAppIcon()
+    private fun cacheIconDescriptor(entry: NotificationEntry, descriptor: StatusBarIcon) {
+        if (
+            android.app.Flags.notificationsUseAppIcon() ||
+                android.app.Flags.notificationsUseMonochromeAppIcon()
         ) {
             // If either of the new icon flags is enabled, we cache the icon all the time.
-            if (showPeopleAvatar) {
-                entry.icons.peopleAvatarDescriptor = descriptor
-            } else if (usingMonochromeAppIcon) {
+            when (descriptor.type) {
+                StatusBarIcon.Type.PeopleAvatar -> entry.icons.peopleAvatarDescriptor = descriptor
                 // When notificationsUseMonochromeAppIcon is enabled, we use the appIconDescriptor.
-                entry.icons.appIconDescriptor = descriptor
-            } else {
+                StatusBarIcon.Type.MaybeMonochromeAppIcon ->
+                    entry.icons.appIconDescriptor = descriptor
                 // When notificationsUseAppIcon is enabled, the app icon overrides the small icon.
                 // But either way, it's a good idea to cache the descriptor.
-                entry.icons.smallIconDescriptor = descriptor
+                else -> entry.icons.smallIconDescriptor = descriptor
             }
         } else if (isImportantConversation(entry)) {
             // Old approach: cache only if important conversation.
-            if (showPeopleAvatar) {
+            if (descriptor.type == StatusBarIcon.Type.PeopleAvatar) {
                 entry.icons.peopleAvatarDescriptor = descriptor
             } else {
                 entry.icons.smallIconDescriptor = descriptor
@@ -312,7 +299,10 @@
         }
     }
 
-    private fun Icon.toStatusBarIcon(entry: NotificationEntry): StatusBarIcon {
+    private fun Icon.toStatusBarIcon(
+        entry: NotificationEntry,
+        type: StatusBarIcon.Type
+    ): StatusBarIcon {
         val n = entry.sbn.notification
         return StatusBarIcon(
             entry.sbn.user,
@@ -320,33 +310,11 @@
             /* icon = */ this,
             n.iconLevel,
             n.number,
-            iconBuilder.getIconContentDescription(n)
+            iconBuilder.getIconContentDescription(n),
+            type
         )
     }
 
-    // TODO(b/335211019): Should we merge this with the method in GroupHelper?
-    private fun getMonochromeAppIcon(entry: NotificationEntry): Icon? {
-        // TODO(b/335211019): This should be done in the background.
-        var monochromeIcon: Icon? = null
-        try {
-            val appIcon: Drawable = iconBuilder.getAppIcon(entry.sbn.notification)
-            if (appIcon is AdaptiveIconDrawable) {
-                if (appIcon.monochrome != null) {
-                    monochromeIcon =
-                        Icon.createWithResourceAdaptiveDrawable(
-                            /* resPackage = */ entry.sbn.packageName,
-                            /* resId = */ appIcon.sourceDrawableResId,
-                            /* useMonochrome = */ true,
-                            /* inset = */ -3.0f * AdaptiveIconDrawable.getExtraInsetFraction()
-                        )
-                }
-            }
-        } catch (e: Exception) {
-            Log.e(TAG, "Failed to getAppIcon() in getMonochromeAppIcon()", e)
-        }
-        return monochromeIcon
-    }
-
     private suspend fun getLauncherShortcutIconForPeopleAvatar(entry: NotificationEntry) =
         withContext(bgCoroutineContext) {
             var icon: Icon? = null
@@ -365,7 +333,8 @@
             // Once we have the icon, updating it should happen on the main thread.
             if (icon != null) {
                 withContext(mainCoroutineContext) {
-                    val iconDescriptor = icon.toStatusBarIcon(entry)
+                    val iconDescriptor =
+                        icon.toStatusBarIcon(entry, StatusBarIcon.Type.PeopleAvatar)
 
                     // Cache the value
                     entry.icons.peopleAvatarDescriptor = iconDescriptor
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
index c29d700..a8fd082 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
@@ -24,6 +24,7 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.interruption.AvalancheSuppressor.AvalancheEvent
 import javax.inject.Inject
 
 // Class to track avalanche trigger event time.
@@ -31,37 +32,41 @@
 class AvalancheProvider
 @Inject
 constructor(
-        private val broadcastDispatcher: BroadcastDispatcher,
-        private val logger: VisualInterruptionDecisionLogger,
-        private val uiEventLogger: UiEventLogger,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val logger: VisualInterruptionDecisionLogger,
+    private val uiEventLogger: UiEventLogger,
 ) {
     val TAG = "AvalancheProvider"
     val timeoutMs = 120000
     var startTime: Long = 0L
 
-    private val avalancheTriggerIntents = mutableSetOf(
+    private val avalancheTriggerIntents =
+        mutableSetOf(
             Intent.ACTION_AIRPLANE_MODE_CHANGED,
             Intent.ACTION_BOOT_COMPLETED,
             Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
             Intent.ACTION_USER_SWITCHED
-    )
+        )
 
-    private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
-        override fun onReceive(context: Context, intent: Intent) {
-            if (intent.action in avalancheTriggerIntents) {
+    private val broadcastReceiver: BroadcastReceiver =
+        object : BroadcastReceiver() {
+            override fun onReceive(context: Context, intent: Intent) {
+                if (intent.action in avalancheTriggerIntents) {
 
-                // Ignore when airplane mode turned on
-                if (intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED
-                        && intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false)) {
-                    Log.d(TAG, "broadcastReceiver: ignore airplane mode on")
-                    return
+                    // Ignore when airplane mode turned on
+                    if (
+                        intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED &&
+                            intent.getBooleanExtra(/* name= */ "state", /* defaultValue */ false)
+                    ) {
+                        Log.d(TAG, "broadcastReceiver: ignore airplane mode on")
+                        return
+                    }
+                    Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action)
+                    uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_RECEIVED_TRIGGERING_EVENT)
+                    startTime = System.currentTimeMillis()
                 }
-                Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action)
-                uiEventLogger.log(AvalancheSuppressor.AvalancheEvent.START);
-                startTime = System.currentTimeMillis()
             }
         }
-    }
 
     fun register() {
         val intentFilter = IntentFilter()
@@ -70,4 +75,4 @@
         }
         broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index f84b5f4..367aaad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -270,32 +270,26 @@
     }
 
     enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum {
-        @UiEvent(
-            doc =
-                "An avalanche event occurred but this notification was suppressed by a " +
-                    "non-avalanche suppressor."
-        )
-        START(1802),
-        @UiEvent(doc = "HUN was suppressed in avalanche.") SUPPRESS(1803),
-        @UiEvent(doc = "HUN allowed during avalanche because it is high priority.")
-        ALLOW_CONVERSATION_AFTER_AVALANCHE(1804),
-        @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.")
-        ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805),
-        @UiEvent(doc = "HUN allowed during avalanche because it is a call.") ALLOW_CALLSTYLE(1806),
+        @UiEvent(doc = "An avalanche event occurred, and a suppression period will start now.")
+        AVALANCHE_SUPPRESSOR_RECEIVED_TRIGGERING_EVENT(1824),
+        @UiEvent(doc = "HUN was suppressed in avalanche.")
+        AVALANCHE_SUPPRESSOR_HUN_SUPPRESSED(1825),
+        @UiEvent(doc = "HUN allowed during avalanche because conversation newer than the trigger.")
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_NEW_CONVERSATION(1826),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a priority conversation.")
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_PRIORITY_CONVERSATION(1827),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a CallStyle notification.")
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CALL_STYLE(1828),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a reminder notification.")
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_REMINDER(1829),
         @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
-        ALLOW_CATEGORY_REMINDER(1807),
-        @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
-        ALLOW_CATEGORY_EVENT(1808),
-        @UiEvent(
-            doc =
-                "HUN allowed during avalanche because it has a full screen intent and " +
-                    "the full screen intent permission is granted."
-        )
-        ALLOW_FSI_WITH_PERMISSION_ON(1809),
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT(1830),
+        @UiEvent(doc = "HUN allowed during avalanche because it has FSI.")
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_FSI_WITH_PERMISSION(1831),
         @UiEvent(doc = "HUN allowed during avalanche because it is colorized.")
-        ALLOW_COLORIZED(1810),
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED(1832),
         @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.")
-        ALLOW_EMERGENCY(1811);
+        AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY(1833);
 
         override fun getId(): Int {
             return id
@@ -323,46 +317,46 @@
             entry.ranking.isConversation &&
                 entry.sbn.notification.getWhen() > avalancheProvider.startTime
         ) {
-            uiEventLogger.log(AvalancheEvent.ALLOW_CONVERSATION_AFTER_AVALANCHE)
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_NEW_CONVERSATION)
             return State.ALLOW_CONVERSATION_AFTER_AVALANCHE
         }
 
         if (entry.channel?.isImportantConversation == true) {
-            uiEventLogger.log(AvalancheEvent.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME)
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_PRIORITY_CONVERSATION)
             return State.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME
         }
 
         if (entry.sbn.notification.isStyle(Notification.CallStyle::class.java)) {
-            uiEventLogger.log(AvalancheEvent.ALLOW_CALLSTYLE)
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CALL_STYLE)
             return State.ALLOW_CALLSTYLE
         }
 
         if (entry.sbn.notification.category == CATEGORY_REMINDER) {
-            uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_REMINDER)
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_REMINDER)
             return State.ALLOW_CATEGORY_REMINDER
         }
 
         if (entry.sbn.notification.category == CATEGORY_EVENT) {
-            uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_EVENT)
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_CATEGORY_EVENT)
             return State.ALLOW_CATEGORY_EVENT
         }
 
         if (entry.sbn.notification.fullScreenIntent != null) {
-            uiEventLogger.log(AvalancheEvent.ALLOW_FSI_WITH_PERMISSION_ON)
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_FSI_WITH_PERMISSION)
             return State.ALLOW_FSI_WITH_PERMISSION_ON
         }
         if (entry.sbn.notification.isColorized) {
-            uiEventLogger.log(AvalancheEvent.ALLOW_COLORIZED)
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_COLORIZED)
             return State.ALLOW_COLORIZED
         }
         if (
             packageManager.checkPermission(RECEIVE_EMERGENCY_BROADCAST, entry.sbn.packageName) ==
                 PERMISSION_GRANTED
         ) {
-            uiEventLogger.log(AvalancheEvent.ALLOW_EMERGENCY)
+            uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_ALLOWED_EMERGENCY)
             return State.ALLOW_EMERGENCY
         }
-        uiEventLogger.log(AvalancheEvent.SUPPRESS)
+        uiEventLogger.log(AvalancheEvent.AVALANCHE_SUPPRESSOR_HUN_SUPPRESSED)
         return State.SUPPRESS
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 6ba26d9..af5117e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -56,6 +56,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
+import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder;
 import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder;
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel;
@@ -105,6 +106,7 @@
             NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
             HeadsUpStyleProvider headsUpStyleProvider,
             NotificationRowContentBinderLogger logger) {
+        NotificationRowContentBinderRefactor.assertInLegacyMode();
         mRemoteViewCache = remoteViewCache;
         mRemoteInputManager = remoteInputManager;
         mConversationProcessor = conversationProcessor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index c1302a0..fe0ee52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -147,11 +147,6 @@
          * Use increased height when binding heads up views.
          */
         public boolean usesIncreasedHeadsUpHeight;
-
-        /**
-         * Is group summary notification
-         */
-        public boolean mIsGroupSummary;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
new file mode 100644
index 0000000..e704140
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -0,0 +1,1569 @@
+/*
+ * 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
+ */
+package com.android.systemui.statusbar.notification.row
+
+import android.annotation.SuppressLint
+import android.app.Notification
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.os.AsyncTask
+import android.os.Build
+import android.os.CancellationSignal
+import android.os.Trace
+import android.os.UserHandle
+import android.service.notification.StatusBarNotification
+import android.util.Log
+import android.view.NotificationHeaderView
+import android.view.View
+import android.view.ViewGroup
+import android.widget.RemoteViews
+import android.widget.RemoteViews.InteractionHandler
+import android.widget.RemoteViews.OnViewAppliedListener
+import com.android.app.tracing.TraceUtils
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.widget.ImageMessageConsumer
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.NotifInflation
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.InflationTask
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
+import com.android.systemui.statusbar.notification.InflationException
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_GROUP_SUMMARY_HEADER
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
+import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation
+import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel
+import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews
+import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel
+import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
+import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineConversationViewBinder
+import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState
+import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder
+import com.android.systemui.statusbar.policy.SmartReplyStateInflater
+import com.android.systemui.util.Assert
+import java.util.concurrent.Executor
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * [NotificationRowContentBinderImpl] binds content to a [ExpandableNotificationRow] by
+ * asynchronously building the content's [RemoteViews] and applying it to the row.
+ */
+@SysUISingleton
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+class NotificationRowContentBinderImpl
+@Inject
+constructor(
+    private val remoteViewCache: NotifRemoteViewCache,
+    private val remoteInputManager: NotificationRemoteInputManager,
+    private val conversationProcessor: ConversationNotificationProcessor,
+    @NotifInflation private val inflationExecutor: Executor,
+    private val smartReplyStateInflater: SmartReplyStateInflater,
+    private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
+    private val headsUpStyleProvider: HeadsUpStyleProvider,
+    private val logger: NotificationRowContentBinderLogger
+) : NotificationRowContentBinder {
+
+    init {
+        /* check if */ NotificationRowContentBinderRefactor.isUnexpectedlyInLegacyMode()
+    }
+
+    private var inflateSynchronously = false
+
+    override fun bindContent(
+        entry: NotificationEntry,
+        row: ExpandableNotificationRow,
+        @InflationFlag contentToBind: Int,
+        bindParams: BindParams,
+        forceInflate: Boolean,
+        callback: InflationCallback?
+    ) {
+        if (row.isRemoved) {
+            // We don't want to reinflate anything for removed notifications. Otherwise views might
+            // be readded to the stack, leading to leaks. This may happen with low-priority groups
+            // where the removal of already removed children can lead to a reinflation.
+            logger.logNotBindingRowWasRemoved(entry)
+            return
+        }
+        logger.logBinding(entry, contentToBind)
+        val sbn: StatusBarNotification = entry.sbn
+
+        // To check if the notification has inline image and preload inline image if necessary.
+        row.imageResolver.preloadImages(sbn.notification)
+        if (forceInflate) {
+            remoteViewCache.clearCache(entry)
+        }
+
+        // Cancel any pending frees on any view we're trying to bind since we should be bound after.
+        cancelContentViewFrees(row, contentToBind)
+        val task =
+            AsyncInflationTask(
+                inflationExecutor,
+                inflateSynchronously,
+                /* reInflateFlags = */ contentToBind,
+                remoteViewCache,
+                entry,
+                conversationProcessor,
+                row,
+                bindParams.isMinimized,
+                bindParams.usesIncreasedHeight,
+                bindParams.usesIncreasedHeadsUpHeight,
+                callback,
+                remoteInputManager.remoteViewsOnClickHandler,
+                /* isMediaFlagEnabled = */ smartReplyStateInflater,
+                notifLayoutInflaterFactoryProvider,
+                headsUpStyleProvider,
+                logger
+            )
+        if (inflateSynchronously) {
+            task.onPostExecute(task.doInBackground())
+        } else {
+            task.executeOnExecutor(inflationExecutor)
+        }
+    }
+
+    @VisibleForTesting
+    fun inflateNotificationViews(
+        entry: NotificationEntry,
+        row: ExpandableNotificationRow,
+        bindParams: BindParams,
+        inflateSynchronously: Boolean,
+        @InflationFlag reInflateFlags: Int,
+        builder: Notification.Builder,
+        packageContext: Context,
+        smartRepliesInflater: SmartReplyStateInflater
+    ): InflationProgress {
+        val systemUIContext = row.context
+        val result =
+            beginInflationAsync(
+                reInflateFlags = reInflateFlags,
+                entry = entry,
+                builder = builder,
+                isMinimized = bindParams.isMinimized,
+                usesIncreasedHeight = bindParams.usesIncreasedHeight,
+                usesIncreasedHeadsUpHeight = bindParams.usesIncreasedHeadsUpHeight,
+                systemUIContext = systemUIContext,
+                packageContext = packageContext,
+                row = row,
+                notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
+                headsUpStyleProvider = headsUpStyleProvider,
+                conversationProcessor = conversationProcessor,
+                logger = logger,
+            )
+        inflateSmartReplyViews(
+            result,
+            reInflateFlags,
+            entry,
+            systemUIContext,
+            packageContext,
+            row.existingSmartReplyState,
+            smartRepliesInflater,
+            logger,
+        )
+        if (AsyncHybridViewInflation.isEnabled) {
+            result.inflatedSingleLineView =
+                result.contentModel.singleLineViewModel?.let { viewModel ->
+                    SingleLineViewInflater.inflateSingleLineViewHolder(
+                        viewModel.isConversation(),
+                        reInflateFlags,
+                        entry,
+                        systemUIContext,
+                        logger,
+                    )
+                }
+        }
+        apply(
+            inflationExecutor,
+            inflateSynchronously,
+            bindParams.isMinimized,
+            result,
+            reInflateFlags,
+            remoteViewCache,
+            entry,
+            row,
+            remoteInputManager.remoteViewsOnClickHandler,
+            /* callback= */ null,
+            logger
+        )
+        return result
+    }
+
+    override fun cancelBind(entry: NotificationEntry, row: ExpandableNotificationRow): Boolean {
+        val abortedTask: Boolean = entry.abortTask()
+        if (abortedTask) {
+            logger.logCancelBindAbortedTask(entry)
+        }
+        return abortedTask
+    }
+
+    @SuppressLint("WrongConstant")
+    override fun unbindContent(
+        entry: NotificationEntry,
+        row: ExpandableNotificationRow,
+        @InflationFlag contentToUnbind: Int
+    ) {
+        logger.logUnbinding(entry, contentToUnbind)
+        var curFlag = 1
+        var contentLeftToUnbind = contentToUnbind
+        while (contentLeftToUnbind != 0) {
+            if (contentLeftToUnbind and curFlag != 0) {
+                freeNotificationView(entry, row, curFlag)
+            }
+            contentLeftToUnbind = contentLeftToUnbind and curFlag.inv()
+            curFlag = curFlag shl 1
+        }
+    }
+
+    /**
+     * Frees the content view associated with the inflation flag as soon as the view is not showing.
+     *
+     * @param inflateFlag the flag corresponding to the content view which should be freed
+     */
+    private fun freeNotificationView(
+        entry: NotificationEntry,
+        row: ExpandableNotificationRow,
+        @InflationFlag inflateFlag: Int
+    ) {
+        when (inflateFlag) {
+            FLAG_CONTENT_VIEW_CONTRACTED ->
+                row.privateLayout.performWhenContentInactive(
+                    NotificationContentView.VISIBLE_TYPE_CONTRACTED
+                ) {
+                    row.privateLayout.setContractedChild(null)
+                    remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
+                }
+            FLAG_CONTENT_VIEW_EXPANDED ->
+                row.privateLayout.performWhenContentInactive(
+                    NotificationContentView.VISIBLE_TYPE_EXPANDED
+                ) {
+                    row.privateLayout.setExpandedChild(null)
+                    remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
+                }
+            FLAG_CONTENT_VIEW_HEADS_UP ->
+                row.privateLayout.performWhenContentInactive(
+                    NotificationContentView.VISIBLE_TYPE_HEADSUP
+                ) {
+                    row.privateLayout.setHeadsUpChild(null)
+                    remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
+                    row.privateLayout.setHeadsUpInflatedSmartReplies(null)
+                }
+            FLAG_CONTENT_VIEW_PUBLIC ->
+                row.publicLayout.performWhenContentInactive(
+                    NotificationContentView.VISIBLE_TYPE_CONTRACTED
+                ) {
+                    row.publicLayout.setContractedChild(null)
+                    remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)
+                }
+            FLAG_CONTENT_VIEW_SINGLE_LINE -> {
+                if (AsyncHybridViewInflation.isEnabled) {
+                    row.privateLayout.performWhenContentInactive(
+                        NotificationContentView.VISIBLE_TYPE_SINGLELINE
+                    ) {
+                        row.privateLayout.setSingleLineView(null)
+                    }
+                }
+            }
+            else -> {}
+        }
+    }
+
+    /**
+     * Cancel any pending content view frees from [.freeNotificationView] for the provided content
+     * views.
+     *
+     * @param row top level notification row containing the content views
+     * @param contentViews content views to cancel pending frees on
+     */
+    private fun cancelContentViewFrees(
+        row: ExpandableNotificationRow,
+        @InflationFlag contentViews: Int
+    ) {
+        if (contentViews and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
+            row.privateLayout.removeContentInactiveRunnable(
+                NotificationContentView.VISIBLE_TYPE_CONTRACTED
+            )
+        }
+        if (contentViews and FLAG_CONTENT_VIEW_EXPANDED != 0) {
+            row.privateLayout.removeContentInactiveRunnable(
+                NotificationContentView.VISIBLE_TYPE_EXPANDED
+            )
+        }
+        if (contentViews and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
+            row.privateLayout.removeContentInactiveRunnable(
+                NotificationContentView.VISIBLE_TYPE_HEADSUP
+            )
+        }
+        if (contentViews and FLAG_CONTENT_VIEW_PUBLIC != 0) {
+            row.publicLayout.removeContentInactiveRunnable(
+                NotificationContentView.VISIBLE_TYPE_CONTRACTED
+            )
+        }
+        if (
+            AsyncHybridViewInflation.isEnabled &&
+                contentViews and FLAG_CONTENT_VIEW_SINGLE_LINE != 0
+        ) {
+            row.privateLayout.removeContentInactiveRunnable(
+                NotificationContentView.VISIBLE_TYPE_SINGLELINE
+            )
+        }
+    }
+
+    /**
+     * Sets whether to perform inflation on the same thread as the caller. This method should only
+     * be used in tests, not in production.
+     */
+    @VisibleForTesting
+    override fun setInflateSynchronously(inflateSynchronously: Boolean) {
+        this.inflateSynchronously = inflateSynchronously
+    }
+
+    class AsyncInflationTask(
+        private val inflationExecutor: Executor,
+        private val inflateSynchronously: Boolean,
+        @get:InflationFlag @get:VisibleForTesting @InflationFlag val reInflateFlags: Int,
+        private val remoteViewCache: NotifRemoteViewCache,
+        private val entry: NotificationEntry,
+        private val conversationProcessor: ConversationNotificationProcessor,
+        private val row: ExpandableNotificationRow,
+        private val isMinimized: Boolean,
+        private val usesIncreasedHeight: Boolean,
+        private val usesIncreasedHeadsUpHeight: Boolean,
+        private val callback: InflationCallback?,
+        private val remoteViewClickHandler: InteractionHandler?,
+        private val smartRepliesInflater: SmartReplyStateInflater,
+        private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
+        private val headsUpStyleProvider: HeadsUpStyleProvider,
+        private val logger: NotificationRowContentBinderLogger
+    ) : AsyncTask<Void, Void, Result<InflationProgress>>(), InflationCallback, InflationTask {
+        private val context: Context
+            get() = row.context
+
+        private var cancellationSignal: CancellationSignal? = null
+
+        init {
+            entry.setInflationTask(this)
+        }
+
+        private fun updateApplicationInfo(sbn: StatusBarNotification) {
+            val packageName: String = sbn.packageName
+            val userId: Int = UserHandle.getUserId(sbn.uid)
+            val appInfo: ApplicationInfo
+            try {
+                // This method has an internal cache, so we don't need to add our own caching here.
+                appInfo =
+                    context.packageManager.getApplicationInfoAsUser(
+                        packageName,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                        userId
+                    )
+            } catch (e: PackageManager.NameNotFoundException) {
+                return
+            }
+            Notification.addFieldsFromContext(appInfo, sbn.notification)
+        }
+
+        override fun onPreExecute() {
+            Trace.beginAsyncSection(ASYNC_TASK_TRACE_METHOD, System.identityHashCode(this))
+        }
+
+        public override fun doInBackground(vararg params: Void): Result<InflationProgress> {
+            return TraceUtils.trace(
+                "NotificationContentInflater.AsyncInflationTask#doInBackground"
+            ) {
+                try {
+                    return@trace Result.success(doInBackgroundInternal())
+                } catch (e: Exception) {
+                    logger.logAsyncTaskException(entry, "inflating", e)
+                    return@trace Result.failure(e)
+                }
+            }
+        }
+
+        private fun doInBackgroundInternal(): InflationProgress {
+            val sbn: StatusBarNotification = entry.sbn
+            // Ensure the ApplicationInfo is updated before a builder is recovered.
+            updateApplicationInfo(sbn)
+            val recoveredBuilder = Notification.Builder.recoverBuilder(context, sbn.notification)
+            var packageContext: Context = sbn.getPackageContext(context)
+            if (recoveredBuilder.usesTemplate()) {
+                // For all of our templates, we want it to be RTL
+                packageContext = RtlEnabledContext(packageContext)
+            }
+            val inflationProgress =
+                beginInflationAsync(
+                    reInflateFlags = reInflateFlags,
+                    entry = entry,
+                    builder = recoveredBuilder,
+                    isMinimized = isMinimized,
+                    usesIncreasedHeight = usesIncreasedHeight,
+                    usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight,
+                    systemUIContext = context,
+                    packageContext = packageContext,
+                    row = row,
+                    notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
+                    headsUpStyleProvider = headsUpStyleProvider,
+                    conversationProcessor = conversationProcessor,
+                    logger = logger
+                )
+            logger.logAsyncTaskProgress(
+                entry,
+                "getting existing smart reply state (on wrong thread!)"
+            )
+            val previousSmartReplyState: InflatedSmartReplyState? = row.existingSmartReplyState
+            logger.logAsyncTaskProgress(entry, "inflating smart reply views")
+            inflateSmartReplyViews(
+                /* result = */ inflationProgress,
+                reInflateFlags,
+                entry,
+                context,
+                packageContext,
+                previousSmartReplyState,
+                smartRepliesInflater,
+                logger,
+            )
+            if (AsyncHybridViewInflation.isEnabled) {
+                logger.logAsyncTaskProgress(entry, "inflating single line view")
+                inflationProgress.inflatedSingleLineView =
+                    inflationProgress.contentModel.singleLineViewModel?.let {
+                        SingleLineViewInflater.inflateSingleLineViewHolder(
+                            it.isConversation(),
+                            reInflateFlags,
+                            entry,
+                            context,
+                            logger
+                        )
+                    }
+            }
+            logger.logAsyncTaskProgress(entry, "getting row image resolver (on wrong thread!)")
+            val imageResolver = row.imageResolver
+            // wait for image resolver to finish preloading
+            logger.logAsyncTaskProgress(entry, "waiting for preloaded images")
+            imageResolver.waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS)
+            return inflationProgress
+        }
+
+        public override fun onPostExecute(result: Result<InflationProgress>) {
+            Trace.endAsyncSection(ASYNC_TASK_TRACE_METHOD, System.identityHashCode(this))
+            result
+                .onSuccess { progress ->
+                    // Logged in detail in apply.
+                    cancellationSignal =
+                        apply(
+                            inflationExecutor,
+                            inflateSynchronously,
+                            isMinimized,
+                            progress,
+                            reInflateFlags,
+                            remoteViewCache,
+                            entry,
+                            row,
+                            remoteViewClickHandler,
+                            this /* callback */,
+                            logger
+                        )
+                }
+                .onFailure { error -> handleError(error as Exception) }
+        }
+
+        override fun onCancelled(result: Result<InflationProgress>) {
+            Trace.endAsyncSection(ASYNC_TASK_TRACE_METHOD, System.identityHashCode(this))
+        }
+
+        private fun handleError(e: Exception) {
+            entry.onInflationTaskFinished()
+            val sbn: StatusBarNotification = entry.sbn
+            val ident: String = (sbn.packageName + "/0x" + Integer.toHexString(sbn.id))
+            Log.e(TAG, "couldn't inflate view for notification $ident", e)
+            callback?.handleInflationException(
+                row.entry,
+                InflationException("Couldn't inflate contentViews$e")
+            )
+
+            // Cancel any image loading tasks, not useful any more
+            row.imageResolver.cancelRunningTasks()
+        }
+
+        override fun abort() {
+            logger.logAsyncTaskProgress(entry, "cancelling inflate")
+            cancel(/* mayInterruptIfRunning= */ true)
+            if (cancellationSignal != null) {
+                logger.logAsyncTaskProgress(entry, "cancelling apply")
+                cancellationSignal!!.cancel()
+            }
+            logger.logAsyncTaskProgress(entry, "aborted")
+        }
+
+        override fun handleInflationException(entry: NotificationEntry, e: Exception) {
+            handleError(e)
+        }
+
+        override fun onAsyncInflationFinished(entry: NotificationEntry) {
+            this.entry.onInflationTaskFinished()
+            row.onNotificationUpdated()
+            callback?.onAsyncInflationFinished(this.entry)
+
+            // Notify the resolver that the inflation task has finished,
+            // try to purge unnecessary cached entries.
+            row.imageResolver.purgeCache()
+
+            // Cancel any image loading tasks that have not completed at this point
+            row.imageResolver.cancelRunningTasks()
+        }
+
+        class RtlEnabledContext(packageContext: Context) : ContextWrapper(packageContext) {
+            override fun getApplicationInfo(): ApplicationInfo {
+                val applicationInfo = ApplicationInfo(super.getApplicationInfo())
+                applicationInfo.flags = applicationInfo.flags or ApplicationInfo.FLAG_SUPPORTS_RTL
+                return applicationInfo
+            }
+        }
+
+        companion object {
+            private const val IMG_PRELOAD_TIMEOUT_MS = 1000L
+        }
+    }
+
+    @VisibleForTesting
+    class InflationProgress(
+        @VisibleForTesting val packageContext: Context,
+        val remoteViews: NewRemoteViews,
+        val contentModel: NotificationContentModel,
+    ) {
+
+        var inflatedContentView: View? = null
+        var inflatedHeadsUpView: View? = null
+        var inflatedExpandedView: View? = null
+        var inflatedPublicView: View? = null
+        var inflatedGroupHeaderView: NotificationHeaderView? = null
+        var inflatedMinimizedGroupHeaderView: NotificationHeaderView? = null
+        var inflatedSmartReplyState: InflatedSmartReplyState? = null
+        var expandedInflatedSmartReplies: InflatedSmartReplyViewHolder? = null
+        var headsUpInflatedSmartReplies: InflatedSmartReplyViewHolder? = null
+
+        // Inflated SingleLineView that lacks the UI State
+        var inflatedSingleLineView: HybridNotificationView? = null
+    }
+
+    @VisibleForTesting
+    abstract class ApplyCallback {
+        abstract fun setResultView(v: View)
+
+        abstract val remoteView: RemoteViews
+    }
+
+    companion object {
+        const val TAG = "NotifContentInflater"
+
+        private fun inflateSmartReplyViews(
+            result: InflationProgress,
+            @InflationFlag reInflateFlags: Int,
+            entry: NotificationEntry,
+            context: Context,
+            packageContext: Context,
+            previousSmartReplyState: InflatedSmartReplyState?,
+            inflater: SmartReplyStateInflater,
+            logger: NotificationRowContentBinderLogger
+        ) {
+            val inflateContracted =
+                (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0 &&
+                    result.remoteViews.contracted != null)
+            val inflateExpanded =
+                (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0 &&
+                    result.remoteViews.expanded != null)
+            val inflateHeadsUp =
+                (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0 &&
+                    result.remoteViews.headsUp != null)
+            if (inflateContracted || inflateExpanded || inflateHeadsUp) {
+                logger.logAsyncTaskProgress(entry, "inflating contracted smart reply state")
+                result.inflatedSmartReplyState = inflater.inflateSmartReplyState(entry)
+            }
+            if (inflateExpanded) {
+                logger.logAsyncTaskProgress(entry, "inflating expanded smart reply state")
+                result.expandedInflatedSmartReplies =
+                    inflater.inflateSmartReplyViewHolder(
+                        context,
+                        packageContext,
+                        entry,
+                        previousSmartReplyState,
+                        result.inflatedSmartReplyState!!
+                    )
+            }
+            if (inflateHeadsUp) {
+                logger.logAsyncTaskProgress(entry, "inflating heads up smart reply state")
+                result.headsUpInflatedSmartReplies =
+                    inflater.inflateSmartReplyViewHolder(
+                        context,
+                        packageContext,
+                        entry,
+                        previousSmartReplyState,
+                        result.inflatedSmartReplyState!!
+                    )
+            }
+        }
+
+        private fun beginInflationAsync(
+            @InflationFlag reInflateFlags: Int,
+            entry: NotificationEntry,
+            builder: Notification.Builder,
+            isMinimized: Boolean,
+            usesIncreasedHeight: Boolean,
+            usesIncreasedHeadsUpHeight: Boolean,
+            systemUIContext: Context,
+            packageContext: Context,
+            row: ExpandableNotificationRow,
+            notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
+            headsUpStyleProvider: HeadsUpStyleProvider,
+            conversationProcessor: ConversationNotificationProcessor,
+            logger: NotificationRowContentBinderLogger
+        ): InflationProgress {
+            // process conversations and extract the messaging style
+            val messagingStyle =
+                if (entry.ranking.isConversation) {
+                    conversationProcessor.processNotification(entry, builder, logger)
+                } else null
+
+            val remoteViews =
+                createRemoteViews(
+                    reInflateFlags = reInflateFlags,
+                    builder = builder,
+                    isMinimized = isMinimized,
+                    usesIncreasedHeight = usesIncreasedHeight,
+                    usesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight,
+                    row = row,
+                    notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
+                    headsUpStyleProvider = headsUpStyleProvider,
+                    logger = logger,
+                )
+
+            val singleLineViewModel =
+                if (
+                    AsyncHybridViewInflation.isEnabled &&
+                        reInflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE != 0
+                ) {
+                    logger.logAsyncTaskProgress(entry, "inflating single line view model")
+                    SingleLineViewInflater.inflateSingleLineViewModel(
+                        notification = entry.sbn.notification,
+                        messagingStyle = messagingStyle,
+                        builder = builder,
+                        systemUiContext = systemUIContext,
+                    )
+                } else null
+
+            val headsUpStatusBarModel =
+                HeadsUpStatusBarModel(
+                    privateText = builder.getHeadsUpStatusBarText(/* publicMode= */ false),
+                    publicText = builder.getHeadsUpStatusBarText(/* publicMode= */ true),
+                )
+
+            val contentModel =
+                NotificationContentModel(
+                    headsUpStatusBarModel = headsUpStatusBarModel,
+                    singleLineViewModel = singleLineViewModel,
+                )
+
+            return InflationProgress(
+                packageContext = packageContext,
+                remoteViews = remoteViews,
+                contentModel = contentModel,
+            )
+        }
+
+        private fun createRemoteViews(
+            @InflationFlag reInflateFlags: Int,
+            builder: Notification.Builder,
+            isMinimized: Boolean,
+            usesIncreasedHeight: Boolean,
+            usesIncreasedHeadsUpHeight: Boolean,
+            row: ExpandableNotificationRow,
+            notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
+            headsUpStyleProvider: HeadsUpStyleProvider,
+            logger: NotificationRowContentBinderLogger
+        ): NewRemoteViews {
+            return TraceUtils.trace("NotificationContentInflater.createRemoteViews") {
+                val entryForLogging: NotificationEntry = row.entry
+                val contracted =
+                    if (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
+                        logger.logAsyncTaskProgress(
+                            entryForLogging,
+                            "creating contracted remote view"
+                        )
+                        createContentView(builder, isMinimized, usesIncreasedHeight)
+                    } else null
+                val expanded =
+                    if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
+                        logger.logAsyncTaskProgress(
+                            entryForLogging,
+                            "creating expanded remote view"
+                        )
+                        createExpandedView(builder, isMinimized)
+                    } else null
+                val headsUp =
+                    if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
+                        logger.logAsyncTaskProgress(
+                            entryForLogging,
+                            "creating heads up remote view"
+                        )
+                        val isHeadsUpCompact = headsUpStyleProvider.shouldApplyCompactStyle()
+                        if (isHeadsUpCompact) {
+                            builder.createCompactHeadsUpContentView()
+                        } else {
+                            builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight)
+                        }
+                    } else null
+                val public =
+                    if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) {
+                        logger.logAsyncTaskProgress(entryForLogging, "creating public remote view")
+                        builder.makePublicContentView(isMinimized)
+                    } else null
+                val normalGroupHeader =
+                    if (
+                        AsyncGroupHeaderViewInflation.isEnabled &&
+                            reInflateFlags and FLAG_GROUP_SUMMARY_HEADER != 0
+                    ) {
+                        logger.logAsyncTaskProgress(
+                            entryForLogging,
+                            "creating group summary remote view"
+                        )
+                        builder.makeNotificationGroupHeader()
+                    } else null
+                val minimizedGroupHeader =
+                    if (
+                        AsyncGroupHeaderViewInflation.isEnabled &&
+                            reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0
+                    ) {
+                        logger.logAsyncTaskProgress(
+                            entryForLogging,
+                            "creating low-priority group summary remote view"
+                        )
+                        builder.makeLowPriorityContentView(true /* useRegularSubtext */)
+                    } else null
+                NewRemoteViews(
+                        contracted = contracted,
+                        headsUp = headsUp,
+                        expanded = expanded,
+                        public = public,
+                        normalGroupHeader = normalGroupHeader,
+                        minimizedGroupHeader = minimizedGroupHeader
+                    )
+                    .withLayoutInflaterFactory(row, notifLayoutInflaterFactoryProvider)
+            }
+        }
+
+        private fun NewRemoteViews.withLayoutInflaterFactory(
+            row: ExpandableNotificationRow,
+            provider: NotifLayoutInflaterFactory.Provider
+        ): NewRemoteViews {
+            contracted?.let {
+                it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED)
+            }
+            expanded?.let {
+                it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_EXPANDED)
+            }
+            headsUp?.let {
+                it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP)
+            }
+            public?.let {
+                it.layoutInflaterFactory = provider.provide(row, FLAG_CONTENT_VIEW_PUBLIC)
+            }
+            return this
+        }
+
+        private fun apply(
+            inflationExecutor: Executor,
+            inflateSynchronously: Boolean,
+            isMinimized: Boolean,
+            result: InflationProgress,
+            @InflationFlag reInflateFlags: Int,
+            remoteViewCache: NotifRemoteViewCache,
+            entry: NotificationEntry,
+            row: ExpandableNotificationRow,
+            remoteViewClickHandler: InteractionHandler?,
+            callback: InflationCallback?,
+            logger: NotificationRowContentBinderLogger
+        ): CancellationSignal {
+            Trace.beginAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row))
+            val privateLayout = row.privateLayout
+            val publicLayout = row.publicLayout
+            val runningInflations = HashMap<Int, CancellationSignal>()
+            var flag = FLAG_CONTENT_VIEW_CONTRACTED
+            if (reInflateFlags and flag != 0) {
+                val isNewView =
+                    !canReapplyRemoteView(
+                        newView = result.remoteViews.contracted,
+                        oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
+                    )
+                val applyCallback: ApplyCallback =
+                    object : ApplyCallback() {
+                        override fun setResultView(v: View) {
+                            logger.logAsyncTaskProgress(entry, "contracted view applied")
+                            result.inflatedContentView = v
+                        }
+
+                        override val remoteView: RemoteViews
+                            get() = result.remoteViews.contracted!!
+                    }
+                logger.logAsyncTaskProgress(entry, "applying contracted view")
+                applyRemoteView(
+                    inflationExecutor = inflationExecutor,
+                    inflateSynchronously = inflateSynchronously,
+                    isMinimized = isMinimized,
+                    result = result,
+                    reInflateFlags = reInflateFlags,
+                    inflationId = flag,
+                    remoteViewCache = remoteViewCache,
+                    entry = entry,
+                    row = row,
+                    isNewView = isNewView,
+                    remoteViewClickHandler = remoteViewClickHandler,
+                    callback = callback,
+                    parentLayout = privateLayout,
+                    existingView = privateLayout.contractedChild,
+                    existingWrapper =
+                        privateLayout.getVisibleWrapper(
+                            NotificationContentView.VISIBLE_TYPE_CONTRACTED
+                        ),
+                    runningInflations = runningInflations,
+                    applyCallback = applyCallback,
+                    logger = logger
+                )
+            }
+            flag = FLAG_CONTENT_VIEW_EXPANDED
+            if (reInflateFlags and flag != 0) {
+                if (result.remoteViews.expanded != null) {
+                    val isNewView =
+                        !canReapplyRemoteView(
+                            newView = result.remoteViews.expanded,
+                            oldView =
+                                remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
+                        )
+                    val applyCallback: ApplyCallback =
+                        object : ApplyCallback() {
+                            override fun setResultView(v: View) {
+                                logger.logAsyncTaskProgress(entry, "expanded view applied")
+                                result.inflatedExpandedView = v
+                            }
+
+                            override val remoteView: RemoteViews
+                                get() = result.remoteViews.expanded
+                        }
+                    logger.logAsyncTaskProgress(entry, "applying expanded view")
+                    applyRemoteView(
+                        inflationExecutor = inflationExecutor,
+                        inflateSynchronously = inflateSynchronously,
+                        isMinimized = isMinimized,
+                        result = result,
+                        reInflateFlags = reInflateFlags,
+                        inflationId = flag,
+                        remoteViewCache = remoteViewCache,
+                        entry = entry,
+                        row = row,
+                        isNewView = isNewView,
+                        remoteViewClickHandler = remoteViewClickHandler,
+                        callback = callback,
+                        parentLayout = privateLayout,
+                        existingView = privateLayout.expandedChild,
+                        existingWrapper =
+                            privateLayout.getVisibleWrapper(
+                                NotificationContentView.VISIBLE_TYPE_EXPANDED
+                            ),
+                        runningInflations = runningInflations,
+                        applyCallback = applyCallback,
+                        logger = logger
+                    )
+                }
+            }
+            flag = FLAG_CONTENT_VIEW_HEADS_UP
+            if (reInflateFlags and flag != 0) {
+                if (result.remoteViews.headsUp != null) {
+                    val isNewView =
+                        !canReapplyRemoteView(
+                            newView = result.remoteViews.headsUp,
+                            oldView =
+                                remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
+                        )
+                    val applyCallback: ApplyCallback =
+                        object : ApplyCallback() {
+                            override fun setResultView(v: View) {
+                                logger.logAsyncTaskProgress(entry, "heads up view applied")
+                                result.inflatedHeadsUpView = v
+                            }
+
+                            override val remoteView: RemoteViews
+                                get() = result.remoteViews.headsUp
+                        }
+                    logger.logAsyncTaskProgress(entry, "applying heads up view")
+                    applyRemoteView(
+                        inflationExecutor = inflationExecutor,
+                        inflateSynchronously = inflateSynchronously,
+                        isMinimized = isMinimized,
+                        result = result,
+                        reInflateFlags = reInflateFlags,
+                        inflationId = flag,
+                        remoteViewCache = remoteViewCache,
+                        entry = entry,
+                        row = row,
+                        isNewView = isNewView,
+                        remoteViewClickHandler = remoteViewClickHandler,
+                        callback = callback,
+                        parentLayout = privateLayout,
+                        existingView = privateLayout.headsUpChild,
+                        existingWrapper =
+                            privateLayout.getVisibleWrapper(
+                                NotificationContentView.VISIBLE_TYPE_HEADSUP
+                            ),
+                        runningInflations = runningInflations,
+                        applyCallback = applyCallback,
+                        logger = logger
+                    )
+                }
+            }
+            flag = FLAG_CONTENT_VIEW_PUBLIC
+            if (reInflateFlags and flag != 0) {
+                val isNewView =
+                    !canReapplyRemoteView(
+                        newView = result.remoteViews.public,
+                        oldView = remoteViewCache.getCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)
+                    )
+                val applyCallback: ApplyCallback =
+                    object : ApplyCallback() {
+                        override fun setResultView(v: View) {
+                            logger.logAsyncTaskProgress(entry, "public view applied")
+                            result.inflatedPublicView = v
+                        }
+
+                        override val remoteView: RemoteViews
+                            get() = result.remoteViews.public!!
+                    }
+                logger.logAsyncTaskProgress(entry, "applying public view")
+                applyRemoteView(
+                    inflationExecutor = inflationExecutor,
+                    inflateSynchronously = inflateSynchronously,
+                    isMinimized = isMinimized,
+                    result = result,
+                    reInflateFlags = reInflateFlags,
+                    inflationId = flag,
+                    remoteViewCache = remoteViewCache,
+                    entry = entry,
+                    row = row,
+                    isNewView = isNewView,
+                    remoteViewClickHandler = remoteViewClickHandler,
+                    callback = callback,
+                    parentLayout = publicLayout,
+                    existingView = publicLayout.contractedChild,
+                    existingWrapper =
+                        publicLayout.getVisibleWrapper(
+                            NotificationContentView.VISIBLE_TYPE_CONTRACTED
+                        ),
+                    runningInflations = runningInflations,
+                    applyCallback = applyCallback,
+                    logger = logger
+                )
+            }
+            if (AsyncGroupHeaderViewInflation.isEnabled) {
+                val childrenContainer: NotificationChildrenContainer =
+                    row.getChildrenContainerNonNull()
+                if (reInflateFlags and FLAG_GROUP_SUMMARY_HEADER != 0) {
+                    val isNewView =
+                        !canReapplyRemoteView(
+                            newView = result.remoteViews.normalGroupHeader,
+                            oldView =
+                                remoteViewCache.getCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)
+                        )
+                    val applyCallback: ApplyCallback =
+                        object : ApplyCallback() {
+                            override fun setResultView(v: View) {
+                                logger.logAsyncTaskProgress(entry, "group header view applied")
+                                result.inflatedGroupHeaderView = v as NotificationHeaderView?
+                            }
+
+                            override val remoteView: RemoteViews
+                                get() = result.remoteViews.normalGroupHeader!!
+                        }
+                    logger.logAsyncTaskProgress(entry, "applying group header view")
+                    applyRemoteView(
+                        inflationExecutor = inflationExecutor,
+                        inflateSynchronously = inflateSynchronously,
+                        isMinimized = isMinimized,
+                        result = result,
+                        reInflateFlags = reInflateFlags,
+                        inflationId = FLAG_GROUP_SUMMARY_HEADER,
+                        remoteViewCache = remoteViewCache,
+                        entry = entry,
+                        row = row,
+                        isNewView = isNewView,
+                        remoteViewClickHandler = remoteViewClickHandler,
+                        callback = callback,
+                        parentLayout = childrenContainer,
+                        existingView = childrenContainer.groupHeader,
+                        existingWrapper = childrenContainer.notificationHeaderWrapper,
+                        runningInflations = runningInflations,
+                        applyCallback = applyCallback,
+                        logger = logger
+                    )
+                }
+                if (reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) {
+                    val isNewView =
+                        !canReapplyRemoteView(
+                            newView = result.remoteViews.minimizedGroupHeader,
+                            oldView =
+                                remoteViewCache.getCachedView(
+                                    entry,
+                                    FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER
+                                )
+                        )
+                    val applyCallback: ApplyCallback =
+                        object : ApplyCallback() {
+                            override fun setResultView(v: View) {
+                                logger.logAsyncTaskProgress(
+                                    entry,
+                                    "low-priority group header view applied"
+                                )
+                                result.inflatedMinimizedGroupHeaderView =
+                                    v as NotificationHeaderView?
+                            }
+
+                            override val remoteView: RemoteViews
+                                get() = result.remoteViews.minimizedGroupHeader!!
+                        }
+                    logger.logAsyncTaskProgress(entry, "applying low priority group header view")
+                    applyRemoteView(
+                        inflationExecutor = inflationExecutor,
+                        inflateSynchronously = inflateSynchronously,
+                        isMinimized = isMinimized,
+                        result = result,
+                        reInflateFlags = reInflateFlags,
+                        inflationId = FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+                        remoteViewCache = remoteViewCache,
+                        entry = entry,
+                        row = row,
+                        isNewView = isNewView,
+                        remoteViewClickHandler = remoteViewClickHandler,
+                        callback = callback,
+                        parentLayout = childrenContainer,
+                        existingView = childrenContainer.minimizedNotificationHeader,
+                        existingWrapper = childrenContainer.minimizedGroupHeaderWrapper,
+                        runningInflations = runningInflations,
+                        applyCallback = applyCallback,
+                        logger = logger
+                    )
+                }
+            }
+
+            // Let's try to finish, maybe nobody is even inflating anything
+            finishIfDone(
+                result,
+                isMinimized,
+                reInflateFlags,
+                remoteViewCache,
+                runningInflations,
+                callback,
+                entry,
+                row,
+                logger
+            )
+            val cancellationSignal = CancellationSignal()
+            cancellationSignal.setOnCancelListener {
+                logger.logAsyncTaskProgress(entry, "apply cancelled")
+                Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row))
+                runningInflations.values.forEach(
+                    Consumer { obj: CancellationSignal -> obj.cancel() }
+                )
+            }
+            return cancellationSignal
+        }
+
+        @VisibleForTesting
+        fun applyRemoteView(
+            inflationExecutor: Executor?,
+            inflateSynchronously: Boolean,
+            isMinimized: Boolean,
+            result: InflationProgress,
+            @InflationFlag reInflateFlags: Int,
+            @InflationFlag inflationId: Int,
+            remoteViewCache: NotifRemoteViewCache,
+            entry: NotificationEntry,
+            row: ExpandableNotificationRow,
+            isNewView: Boolean,
+            remoteViewClickHandler: InteractionHandler?,
+            callback: InflationCallback?,
+            parentLayout: ViewGroup?,
+            existingView: View?,
+            existingWrapper: NotificationViewWrapper?,
+            runningInflations: HashMap<Int, CancellationSignal>,
+            applyCallback: ApplyCallback,
+            logger: NotificationRowContentBinderLogger
+        ) {
+            val newContentView: RemoteViews = applyCallback.remoteView
+            if (inflateSynchronously) {
+                try {
+                    if (isNewView) {
+                        val v: View =
+                            newContentView.apply(
+                                result.packageContext,
+                                parentLayout,
+                                remoteViewClickHandler
+                            )
+                        validateView(v, entry, row.resources)
+                        applyCallback.setResultView(v)
+                    } else {
+                        requireNotNull(existingView)
+                        requireNotNull(existingWrapper)
+                        newContentView.reapply(
+                            result.packageContext,
+                            existingView,
+                            remoteViewClickHandler
+                        )
+                        validateView(existingView, entry, row.resources)
+                        existingWrapper.onReinflated()
+                    }
+                } catch (e: Exception) {
+                    handleInflationError(
+                        runningInflations,
+                        e,
+                        row.entry,
+                        callback,
+                        logger,
+                        "applying view synchronously"
+                    )
+                    // Add a running inflation to make sure we don't trigger callbacks.
+                    // Safe to do because only happens in tests.
+                    runningInflations[inflationId] = CancellationSignal()
+                }
+                return
+            }
+            val listener: OnViewAppliedListener =
+                object : OnViewAppliedListener {
+                    override fun onViewInflated(v: View) {
+                        if (v is ImageMessageConsumer) {
+                            (v as ImageMessageConsumer).setImageResolver(row.imageResolver)
+                        }
+                    }
+
+                    override fun onViewApplied(v: View) {
+                        val invalidReason = isValidView(v, entry, row.resources)
+                        if (invalidReason != null) {
+                            handleInflationError(
+                                runningInflations,
+                                InflationException(invalidReason),
+                                row.entry,
+                                callback,
+                                logger,
+                                "applied invalid view"
+                            )
+                            runningInflations.remove(inflationId)
+                            return
+                        }
+                        if (isNewView) {
+                            applyCallback.setResultView(v)
+                        } else {
+                            existingWrapper?.onReinflated()
+                        }
+                        runningInflations.remove(inflationId)
+                        finishIfDone(
+                            result,
+                            isMinimized,
+                            reInflateFlags,
+                            remoteViewCache,
+                            runningInflations,
+                            callback,
+                            entry,
+                            row,
+                            logger
+                        )
+                    }
+
+                    override fun onError(e: Exception) {
+                        // Uh oh the async inflation failed. Due to some bugs (see b/38190555), this
+                        // could
+                        // actually also be a system issue, so let's try on the UI thread again to
+                        // be safe.
+                        try {
+                            val newView =
+                                if (isNewView) {
+                                    newContentView.apply(
+                                        result.packageContext,
+                                        parentLayout,
+                                        remoteViewClickHandler
+                                    )
+                                } else {
+                                    newContentView.reapply(
+                                        result.packageContext,
+                                        existingView,
+                                        remoteViewClickHandler
+                                    )
+                                    existingView!!
+                                }
+                            Log.wtf(
+                                TAG,
+                                "Async Inflation failed but normal inflation finished normally.",
+                                e
+                            )
+                            onViewApplied(newView)
+                        } catch (anotherException: Exception) {
+                            runningInflations.remove(inflationId)
+                            handleInflationError(
+                                runningInflations,
+                                e,
+                                row.entry,
+                                callback,
+                                logger,
+                                "applying view"
+                            )
+                        }
+                    }
+                }
+            val cancellationSignal: CancellationSignal =
+                if (isNewView) {
+                    newContentView.applyAsync(
+                        result.packageContext,
+                        parentLayout,
+                        inflationExecutor,
+                        listener,
+                        remoteViewClickHandler
+                    )
+                } else {
+                    newContentView.reapplyAsync(
+                        result.packageContext,
+                        existingView,
+                        inflationExecutor,
+                        listener,
+                        remoteViewClickHandler
+                    )
+                }
+            runningInflations[inflationId] = cancellationSignal
+        }
+
+        /**
+         * Checks if the given View is a valid notification View.
+         *
+         * @return null == valid, non-null == invalid, String represents reason for rejection.
+         */
+        @VisibleForTesting
+        fun isValidView(view: View, entry: NotificationEntry, resources: Resources): String? {
+            return if (!satisfiesMinHeightRequirement(view, entry, resources)) {
+                "inflated notification does not meet minimum height requirement"
+            } else null
+        }
+
+        private fun satisfiesMinHeightRequirement(
+            view: View,
+            entry: NotificationEntry,
+            resources: Resources
+        ): Boolean {
+            return if (!requiresHeightCheck(entry)) {
+                true
+            } else
+                TraceUtils.trace("NotificationContentInflater#satisfiesMinHeightRequirement") {
+                    val heightSpec =
+                        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+                    val referenceWidth =
+                        resources.getDimensionPixelSize(
+                            R.dimen.notification_validation_reference_width
+                        )
+                    val widthSpec =
+                        View.MeasureSpec.makeMeasureSpec(referenceWidth, View.MeasureSpec.EXACTLY)
+                    view.measure(widthSpec, heightSpec)
+                    val minHeight =
+                        resources.getDimensionPixelSize(
+                            R.dimen.notification_validation_minimum_allowed_height
+                        )
+                    view.measuredHeight >= minHeight
+                }
+        }
+
+        /**
+         * Notifications with undecorated custom views need to satisfy a minimum height to avoid
+         * visual issues.
+         */
+        private fun requiresHeightCheck(entry: NotificationEntry): Boolean {
+            // Undecorated custom views are disallowed from S onwards
+            if (entry.targetSdk >= Build.VERSION_CODES.S) {
+                return false
+            }
+            // No need to check if the app isn't using any custom views
+            val notification: Notification = entry.sbn.notification
+            @Suppress("DEPRECATION")
+            return !(notification.contentView == null &&
+                notification.bigContentView == null &&
+                notification.headsUpContentView == null)
+        }
+
+        @Throws(InflationException::class)
+        private fun validateView(view: View, entry: NotificationEntry, resources: Resources) {
+            val invalidReason = isValidView(view, entry, resources)
+            if (invalidReason != null) {
+                throw InflationException(invalidReason)
+            }
+        }
+
+        private fun handleInflationError(
+            runningInflations: HashMap<Int, CancellationSignal>,
+            e: Exception,
+            notification: NotificationEntry,
+            callback: InflationCallback?,
+            logger: NotificationRowContentBinderLogger,
+            logContext: String
+        ) {
+            Assert.isMainThread()
+            logger.logAsyncTaskException(notification, logContext, e)
+            runningInflations.values.forEach(Consumer { obj: CancellationSignal -> obj.cancel() })
+            callback?.handleInflationException(notification, e)
+        }
+
+        /**
+         * Finish the inflation of the views
+         *
+         * @return true if the inflation was finished
+         */
+        private fun finishIfDone(
+            result: InflationProgress,
+            isMinimized: Boolean,
+            @InflationFlag reInflateFlags: Int,
+            remoteViewCache: NotifRemoteViewCache,
+            runningInflations: HashMap<Int, CancellationSignal>,
+            endListener: InflationCallback?,
+            entry: NotificationEntry,
+            row: ExpandableNotificationRow,
+            logger: NotificationRowContentBinderLogger
+        ): Boolean {
+            Assert.isMainThread()
+            if (runningInflations.isNotEmpty()) {
+                return false
+            }
+            val privateLayout = row.privateLayout
+            val publicLayout = row.publicLayout
+            logger.logAsyncTaskProgress(entry, "finishing")
+            if (reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0) {
+                if (result.inflatedContentView != null) {
+                    // New view case
+                    privateLayout.setContractedChild(result.inflatedContentView)
+                    remoteViewCache.putCachedView(
+                        entry,
+                        FLAG_CONTENT_VIEW_CONTRACTED,
+                        result.remoteViews.contracted
+                    )
+                } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)) {
+                    // Reinflation case. Only update if it's still cached (i.e. view has not been
+                    // freed while inflating).
+                    remoteViewCache.putCachedView(
+                        entry,
+                        FLAG_CONTENT_VIEW_CONTRACTED,
+                        result.remoteViews.contracted
+                    )
+                }
+            }
+            if (reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0) {
+                if (result.inflatedExpandedView != null) {
+                    privateLayout.setExpandedChild(result.inflatedExpandedView)
+                    remoteViewCache.putCachedView(
+                        entry,
+                        FLAG_CONTENT_VIEW_EXPANDED,
+                        result.remoteViews.expanded
+                    )
+                } else if (result.remoteViews.expanded == null) {
+                    privateLayout.setExpandedChild(null)
+                    remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
+                } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)) {
+                    remoteViewCache.putCachedView(
+                        entry,
+                        FLAG_CONTENT_VIEW_EXPANDED,
+                        result.remoteViews.expanded
+                    )
+                }
+                if (result.remoteViews.expanded != null) {
+                    privateLayout.setExpandedInflatedSmartReplies(
+                        result.expandedInflatedSmartReplies
+                    )
+                } else {
+                    privateLayout.setExpandedInflatedSmartReplies(null)
+                }
+                row.setExpandable(result.remoteViews.expanded != null)
+            }
+            if (reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0) {
+                if (result.inflatedHeadsUpView != null) {
+                    privateLayout.setHeadsUpChild(result.inflatedHeadsUpView)
+                    remoteViewCache.putCachedView(
+                        entry,
+                        FLAG_CONTENT_VIEW_HEADS_UP,
+                        result.remoteViews.headsUp
+                    )
+                } else if (result.remoteViews.headsUp == null) {
+                    privateLayout.setHeadsUpChild(null)
+                    remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
+                } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)) {
+                    remoteViewCache.putCachedView(
+                        entry,
+                        FLAG_CONTENT_VIEW_HEADS_UP,
+                        result.remoteViews.headsUp
+                    )
+                }
+                if (result.remoteViews.headsUp != null) {
+                    privateLayout.setHeadsUpInflatedSmartReplies(result.headsUpInflatedSmartReplies)
+                } else {
+                    privateLayout.setHeadsUpInflatedSmartReplies(null)
+                }
+            }
+            if (
+                AsyncHybridViewInflation.isEnabled &&
+                    reInflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE != 0
+            ) {
+                val singleLineView = result.inflatedSingleLineView
+                val viewModel = result.contentModel.singleLineViewModel
+                if (singleLineView != null && viewModel != null) {
+                    if (viewModel.isConversation()) {
+                        SingleLineConversationViewBinder.bind(viewModel, singleLineView)
+                    } else {
+                        SingleLineViewBinder.bind(viewModel, singleLineView)
+                    }
+                    privateLayout.setSingleLineView(result.inflatedSingleLineView)
+                }
+            }
+            result.inflatedSmartReplyState?.let { privateLayout.setInflatedSmartReplyState(it) }
+            if (reInflateFlags and FLAG_CONTENT_VIEW_PUBLIC != 0) {
+                if (result.inflatedPublicView != null) {
+                    publicLayout.setContractedChild(result.inflatedPublicView)
+                    remoteViewCache.putCachedView(
+                        entry,
+                        FLAG_CONTENT_VIEW_PUBLIC,
+                        result.remoteViews.public
+                    )
+                } else if (remoteViewCache.hasCachedView(entry, FLAG_CONTENT_VIEW_PUBLIC)) {
+                    remoteViewCache.putCachedView(
+                        entry,
+                        FLAG_CONTENT_VIEW_PUBLIC,
+                        result.remoteViews.public
+                    )
+                }
+            }
+            if (AsyncGroupHeaderViewInflation.isEnabled) {
+                if (reInflateFlags and FLAG_GROUP_SUMMARY_HEADER != 0) {
+                    if (result.inflatedGroupHeaderView != null) {
+                        // We need to set if the row is minimized before setting the group header to
+                        // make sure the setting of header view works correctly
+                        row.setIsMinimized(isMinimized)
+                        row.setGroupHeader(/* headerView= */ result.inflatedGroupHeaderView)
+                        remoteViewCache.putCachedView(
+                            entry,
+                            FLAG_GROUP_SUMMARY_HEADER,
+                            result.remoteViews.normalGroupHeader
+                        )
+                    } else if (remoteViewCache.hasCachedView(entry, FLAG_GROUP_SUMMARY_HEADER)) {
+                        // Re-inflation case. Only update if it's still cached (i.e. view has not
+                        // been freed while inflating).
+                        remoteViewCache.putCachedView(
+                            entry,
+                            FLAG_GROUP_SUMMARY_HEADER,
+                            result.remoteViews.normalGroupHeader
+                        )
+                    }
+                }
+                if (reInflateFlags and FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER != 0) {
+                    if (result.inflatedMinimizedGroupHeaderView != null) {
+                        // We need to set if the row is minimized before setting the group header to
+                        // make sure the setting of header view works correctly
+                        row.setIsMinimized(isMinimized)
+                        row.setMinimizedGroupHeader(
+                            /* headerView= */ result.inflatedMinimizedGroupHeaderView
+                        )
+                        remoteViewCache.putCachedView(
+                            entry,
+                            FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+                            result.remoteViews.minimizedGroupHeader
+                        )
+                    } else if (
+                        remoteViewCache.hasCachedView(entry, FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)
+                    ) {
+                        // Re-inflation case. Only update if it's still cached (i.e. view has not
+                        // been freed while inflating).
+                        remoteViewCache.putCachedView(
+                            entry,
+                            FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER,
+                            result.remoteViews.normalGroupHeader
+                        )
+                    }
+                }
+            }
+            entry.setContentModel(result.contentModel)
+            Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row))
+            endListener?.onAsyncInflationFinished(entry)
+            return true
+        }
+
+        private fun createExpandedView(
+            builder: Notification.Builder,
+            isMinimized: Boolean
+        ): RemoteViews? {
+            @Suppress("DEPRECATION")
+            val bigContentView: RemoteViews? = builder.createBigContentView()
+            if (bigContentView != null) {
+                return bigContentView
+            }
+            if (isMinimized) {
+                @Suppress("DEPRECATION") val contentView: RemoteViews = builder.createContentView()
+                Notification.Builder.makeHeaderExpanded(contentView)
+                return contentView
+            }
+            return null
+        }
+
+        private fun createContentView(
+            builder: Notification.Builder,
+            isMinimized: Boolean,
+            useLarge: Boolean
+        ): RemoteViews {
+            return if (isMinimized) {
+                builder.makeLowPriorityContentView(false /* useRegularSubtext */)
+            } else builder.createContentView(useLarge)
+        }
+
+        /**
+         * @param newView The new view that will be applied
+         * @param oldView The old view that was applied to the existing view before
+         * @return `true` if the RemoteViews are the same and the view can be reused to reapply.
+         */
+        @VisibleForTesting
+        fun canReapplyRemoteView(newView: RemoteViews?, oldView: RemoteViews?): Boolean {
+            return newView == null && oldView == null ||
+                newView != null &&
+                    oldView != null &&
+                    oldView.getPackage() != null &&
+                    newView.getPackage() != null &&
+                    newView.getPackage() == oldView.getPackage() &&
+                    newView.layoutId == oldView.layoutId &&
+                    !oldView.hasFlags(RemoteViews.FLAG_REAPPLY_DISALLOWED)
+        }
+
+        private const val ASYNC_TASK_TRACE_METHOD =
+            "NotificationRowContentBinderImpl.AsyncInflationTask"
+        private const val APPLY_TRACE_METHOD = "NotificationRowContentBinderImpl#apply"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
index 17c2026..84f2f66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java
@@ -17,9 +17,13 @@
 package com.android.systemui.statusbar.notification.row;
 
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 
 import dagger.Binds;
 import dagger.Module;
+import dagger.Provides;
+
+import javax.inject.Provider;
 
 /**
  * Dagger Module containing notification row and view inflation implementations.
@@ -30,10 +34,18 @@
     /**
      * Provides notification row content binder instance.
      */
-    @Binds
+    @Provides
     @SysUISingleton
-    public abstract NotificationRowContentBinder provideNotificationRowContentBinder(
-            NotificationContentInflater contentBinderImpl);
+    public static NotificationRowContentBinder provideNotificationRowContentBinder(
+            Provider<NotificationContentInflater> legacyImpl,
+            Provider<NotificationRowContentBinderImpl> refactoredImpl
+    ) {
+        if (NotificationRowContentBinderRefactor.isEnabled()) {
+            return refactoredImpl.get();
+        } else {
+            return legacyImpl.get();
+        }
+    }
 
     /**
      * Provides notification remote view cache instance.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index 6fc82c9..463192c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -244,7 +244,7 @@
             return SingleIcon(null)
         }
         val userKey = user.getKeyOrName()
-        var conversationIcon: Icon? = null
+        var conversationIcon: Icon? = shortcutIcon
         var conversationText: CharSequence? = conversationTitle
 
         val groups = groupMessages(messages, historicMessages)
@@ -253,10 +253,6 @@
         if (!isGroupConversation) {
             // Conversation is one-to-one, load the single icon
             // Let's resolve the icon / text from the last sender
-            if (shortcutIcon != null) {
-                conversationIcon = shortcutIcon
-            }
-
             for (i in messages.lastIndex downTo 0) {
                 val message = messages[i]
                 val sender = message.senderPerson
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/HeadsUpStatusBarModel.kt
similarity index 76%
rename from packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/HeadsUpStatusBarModel.kt
index 23dbc26..e43ce76 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/HeadsUpStatusBarModel.kt
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recordissue
+package com.android.systemui.statusbar.notification.row.shared
 
-import com.android.traceur.TraceUtils.PresetTraceType
-
-data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
+class HeadsUpStatusBarModel(
+    val privateText: CharSequence,
+    val publicText: CharSequence,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NewRemoteViews.kt
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NewRemoteViews.kt
index 2e9169e..63bba86 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NewRemoteViews.kt
@@ -13,17 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.bluetooth.qsdialog
 
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Binds
-import dagger.Module
+package com.android.systemui.statusbar.notification.row.shared
 
-@Module
-interface BluetoothTileDialogModule {
-    @Binds
-    @SysUISingleton
-    fun bindDeviceItemActionInteractor(
-        impl: DeviceItemActionInteractorImpl
-    ): DeviceItemActionInteractor
-}
+import android.widget.RemoteViews
+
+class NewRemoteViews(
+    val contracted: RemoteViews? = null,
+    val headsUp: RemoteViews? = null,
+    val expanded: RemoteViews? = null,
+    val public: RemoteViews? = null,
+    val normalGroupHeader: RemoteViews? = null,
+    val minimizedGroupHeader: RemoteViews? = null,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
similarity index 66%
copy from packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
index 23dbc26..b2421bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationContentModel.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recordissue
+package com.android.systemui.statusbar.notification.row.shared
 
-import com.android.traceur.TraceUtils.PresetTraceType
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.SingleLineViewModel
 
-data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
+data class NotificationContentModel(
+    val headsUpStatusBarModel: HeadsUpStatusBarModel,
+    val singleLineViewModel: SingleLineViewModel? = null,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index d1fabb1..9394249 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -358,11 +358,7 @@
      * @param whenMillis
      */
     public void setNotificationWhen(long whenMillis) {
-        if (mNotificationHeader == null) {
-            return;
-        }
-
-        final View timeView = mNotificationHeader.findViewById(com.android.internal.R.id.time);
+        final View timeView = mView.findViewById(com.android.internal.R.id.time);
 
         if (timeView instanceof DateTimeView) {
             ((DateTimeView) timeView).setTime(whenMillis);
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 5f4e832..456c321 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
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
 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.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
@@ -63,6 +64,7 @@
      *  Used to read bouncer states.
      */
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private float mStackCutoff;
     private int mScrollY;
     private float mOverScrollTopAmount;
     private float mOverScrollBottomAmount;
@@ -346,6 +348,21 @@
         return mZDistanceBetweenElements;
     }
 
+    /**
+     * Y coordinate in view pixels above which the bottom of the notification stack / shelf / footer
+     * must be.
+     */
+    public float getStackCutoff() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+        return mStackCutoff;
+    }
+
+    /** @see #getStackCutoff() */
+    public void setStackCutoff(float stackCutoff) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        this.mStackCutoff = stackCutoff;
+    }
+
     public int getScrollY() {
         return mScrollY;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
index fc28a99..fabb696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
@@ -13,7 +13,9 @@
         [
             BUCKET_UNKNOWN,
             BUCKET_MEDIA_CONTROLS,
+            BUCKET_TOP_ONGOING,
             BUCKET_HEADS_UP,
+            BUCKET_TOP_UNSEEN,
             BUCKET_FOREGROUND_SERVICE,
             BUCKET_PRIORITY_PEOPLE,
             BUCKET_PEOPLE,
@@ -21,11 +23,28 @@
             BUCKET_SILENT
         ]
 )
-annotation class PriorityBucket
+annotation class PriorityBucket {
+    companion object {
+        fun getAllInOrder(): IntArray =
+            intArrayOf(
+                BUCKET_MEDIA_CONTROLS,
+                BUCKET_TOP_ONGOING,
+                BUCKET_HEADS_UP,
+                BUCKET_TOP_UNSEEN,
+                BUCKET_FOREGROUND_SERVICE,
+                BUCKET_PRIORITY_PEOPLE,
+                BUCKET_PEOPLE,
+                BUCKET_ALERTING,
+                BUCKET_SILENT,
+            )
+    }
+}
 
 const val BUCKET_UNKNOWN = 0
 const val BUCKET_MEDIA_CONTROLS = 1
+const val BUCKET_TOP_ONGOING = 8
 const val BUCKET_HEADS_UP = 2
+const val BUCKET_TOP_UNSEEN = 9
 const val BUCKET_FOREGROUND_SERVICE = 3
 const val BUCKET_PRIORITY_PEOPLE = 7
 const val BUCKET_PEOPLE = 4
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 fe22cc6..0e77ed4 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
@@ -833,6 +833,23 @@
         int y = 0;
         drawDebugInfo(canvas, y, Color.RED, /* label= */ "y = " + y);
 
+        if (SceneContainerFlag.isEnabled()) {
+            y = (int) mScrollViewFields.getStackTop();
+            drawDebugInfo(canvas, y, Color.RED, /* label= */ "getStackTop() = " + y);
+
+            y = (int) mAmbientState.getStackCutoff();
+            drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "getStackCutoff() = " + y);
+
+            y = (int) mScrollViewFields.getHeadsUpTop();
+            drawDebugInfo(canvas, y, Color.GREEN, /* label= */ "getHeadsUpTop() = " + y);
+
+            y += getTopHeadsUpHeight();
+            drawDebugInfo(canvas, y, Color.BLUE,
+                    /* label= */ "getHeadsUpTop() + getTopHeadsUpHeight() = " + y);
+
+            return; // the rest of the fields are not important in Flexiglass
+        }
+
         y = getTopPadding();
         drawDebugInfo(canvas, y, Color.RED, /* label= */ "getTopPadding() = " + y);
 
@@ -1203,8 +1220,8 @@
     }
 
     @Override
-    public void setStackBottom(float stackBottom) {
-        mScrollViewFields.setStackBottom(stackBottom);
+    public void setStackCutoff(float stackCutoff) {
+        mAmbientState.setStackCutoff(stackCutoff);
     }
 
     @Override
@@ -3471,6 +3488,7 @@
             }
 
             if (isUpOrCancel) {
+                mScrollViewFields.sendCurrentGestureOverscroll(false);
                 setIsBeingDragged(false);
             }
             return false;
@@ -3606,7 +3624,6 @@
                 if (mIsBeingDragged) {
                     // Defer actual scrolling to the scene framework if enabled
                     if (SceneContainerFlag.isEnabled()) {
-                        setIsBeingDragged(false);
                         return false;
                     }
                     // Scroll to follow the motion event
@@ -3704,7 +3721,7 @@
 
     protected boolean isInsideQsHeader(MotionEvent ev) {
         if (SceneContainerFlag.isEnabled()) {
-            return ev.getY() < mScrollViewFields.getScrimClippingShape().getBounds().getTop();
+            return ev.getY() < mScrollViewFields.getStackTop();
         }
 
         mQsHeader.getBoundsOnScreen(mQsHeaderBound);
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 6a3055f..d984685 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
@@ -68,7 +68,6 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -1320,7 +1319,10 @@
         updateAlpha();
     }
 
-    void setMaxAlphaFromView(float alpha) {
+    /**
+     * Max alpha from the containing view. Used by brightness slider as an example.
+     */
+    public void setMaxAlphaFromView(float alpha) {
         mMaxAlphaFromView = alpha;
         updateAlpha();
     }
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 4b0b1e0..391bc43 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
@@ -73,7 +73,8 @@
     private var maxNotificationsExcludesMedia = false
 
     /** Whether we allow keyguard to show less important notifications above the shelf. */
-    private var limitLockScreenToImportant = false
+    private val limitLockScreenToOneImportant
+        get() = NotificationMinimalismPrototype.V2.isEnabled
 
     /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
     private var dividerHeight by notNull<Float>()
@@ -89,7 +90,7 @@
     }
 
     private fun allowedByPolicy(stackHeight: StackHeight): Boolean =
-        if (limitLockScreenToImportant && stackHeight.includesLessImportantNotification) {
+        if (stackHeight.shouldForceIntoShelf) {
             log { "\tallowedByPolicy = false" }
             false
         } else {
@@ -333,8 +334,8 @@
         // changes during the lockscreen <=> full shade transition.
         val shelfHeightWithSpaceBefore: Float,
 
-        /** Whether this stack height includes less at least one important notification. */
-        val includesLessImportantNotification: Boolean
+        /** Whether the stack should actually be forced into the shelf before this height. */
+        val shouldForceIntoShelf: Boolean
     )
 
     private fun computeHeightPerNotificationLimit(
@@ -347,7 +348,7 @@
         var previous: ExpandableView? = null
         val onLockscreen = onLockscreen()
 
-        var includesLessImportantNotification = false
+        val counter = if (limitLockScreenToOneImportant) BucketTypeCounter() else null
 
         // Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState).
         yield(
@@ -355,7 +356,7 @@
                 notifsHeight = 0f,
                 notifsHeightSavingSpace = 0f,
                 shelfHeightWithSpaceBefore = shelfHeight,
-                includesLessImportantNotification = includesLessImportantNotification,
+                shouldForceIntoShelf = false,
             )
         )
 
@@ -381,17 +382,9 @@
                     spaceBeforeShelf + shelfHeight
                 }
 
-            if (limitLockScreenToImportant && !includesLessImportantNotification) {
-                val bucket = (currentNotification as? ExpandableNotificationRow)?.entry?.bucket
-                includesLessImportantNotification =
-                    when (bucket) {
-                        null,
-                        BUCKET_MEDIA_CONTROLS,
-                        BUCKET_HEADS_UP,
-                        BUCKET_FOREGROUND_SERVICE,
-                        BUCKET_PRIORITY_PEOPLE -> false
-                        else -> true
-                    }
+            if (counter != null) {
+                val entry = (currentNotification as? ExpandableNotificationRow)?.entry
+                counter.incrementForBucket(entry?.bucket)
             }
 
             log {
@@ -404,7 +397,7 @@
                     notifsHeight = notifications,
                     notifsHeightSavingSpace = notifsWithCollapsedHun,
                     shelfHeightWithSpaceBefore = shelfWithSpaceBefore,
-                    includesLessImportantNotification = includesLessImportantNotification,
+                    shouldForceIntoShelf = counter?.shouldForceIntoShelf() ?: false
                 )
             )
         }
@@ -415,8 +408,6 @@
             infiniteIfNegative(
                 if (NotificationMinimalismPrototype.V1.isEnabled) {
                     NotificationMinimalismPrototype.V1.maxNotifs
-                } else if (NotificationMinimalismPrototype.V2.isEnabled) {
-                    1
                 } else {
                     resources.getInteger(R.integer.keyguard_max_notification_count)
                 }
@@ -424,7 +415,6 @@
         maxNotificationsExcludesMedia =
             NotificationMinimalismPrototype.V1.isEnabled ||
                 NotificationMinimalismPrototype.V2.isEnabled
-        limitLockScreenToImportant = NotificationMinimalismPrototype.V2.isEnabled
 
         dividerHeight =
             max(1f, resources.getDimensionPixelSize(R.dimen.notification_divider_height).toFloat())
@@ -552,4 +542,24 @@
     /** Returns the last index where [predicate] returns true, or -1 if it was always false. */
     private fun <T> Sequence<T>.lastIndexWhile(predicate: (T) -> Boolean): Int =
         takeWhile(predicate).count() - 1
+
+    /** Counts the number of notifications for each type of bucket */
+    data class BucketTypeCounter(
+        var ongoing: Int = 0,
+        var important: Int = 0,
+        var other: Int = 0,
+    ) {
+        fun incrementForBucket(@PriorityBucket bucket: Int?) {
+            when (bucket) {
+                BUCKET_MEDIA_CONTROLS,
+                null -> Unit // not counted as notifications at all
+                BUCKET_TOP_ONGOING -> ongoing++
+                BUCKET_HEADS_UP -> important++
+                BUCKET_TOP_UNSEEN -> important++
+                else -> other++
+            }
+        }
+
+        fun shouldForceIntoShelf(): Boolean = ongoing > 1 || important > 1 || other > 0
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index 6afcf37..2e86ad9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -34,11 +34,6 @@
     var scrimClippingShape: ShadeScrimShape? = null
     /** Y coordinate in view pixels of the top of the notification stack */
     var stackTop: Float = 0f
-    /**
-     * Y coordinate in view pixels above which the bottom of the notification stack / shelf / footer
-     * must be.
-     */
-    var stackBottom: Float = 0f
     /** Y coordinate in view pixels of the top of the HUN */
     var headsUpTop: Float = 0f
     /** Whether the notifications are scrolled all the way to the top (i.e. when freshly opened) */
@@ -70,9 +65,11 @@
     /** send the [syntheticScroll] to the [syntheticScrollConsumer], if present. */
     fun sendSyntheticScroll(syntheticScroll: Float) =
         syntheticScrollConsumer?.accept(syntheticScroll)
+
     /** send [isCurrentGestureOverscroll] to the [currentGestureOverscrollConsumer], if present. */
     fun sendCurrentGestureOverscroll(isCurrentGestureOverscroll: Boolean) =
         currentGestureOverscrollConsumer?.accept(isCurrentGestureOverscroll)
+
     /** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
     fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
 
@@ -80,7 +77,6 @@
         pw.printSection("StackViewStates") {
             pw.println("scrimClippingShape", scrimClippingShape)
             pw.println("stackTop", stackTop)
-            pw.println("stackBottom", stackBottom)
             pw.println("headsUpTop", headsUpTop)
             pw.println("isScrolledToTop", isScrolledToTop)
         }
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 eaaa9a1..762c507 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
@@ -50,8 +50,11 @@
     /** set the y position in px of the top of the stack in this view's coordinates */
     fun setStackTop(stackTop: Float)
 
-    /** set the y position in px of the bottom of the stack in this view's coordinates */
-    fun setStackBottom(stackBottom: Float)
+    /**
+     * set the bottom-most acceptable y-position of the bottom of the notification stack/ shelf /
+     * footer.
+     */
+    fun setStackCutoff(stackBottom: Float)
 
     /** set the y position in px of the top of the HUN in this view's coordinates */
     fun setHeadsUpTop(headsUpTop: Float)
@@ -61,8 +64,10 @@
 
     /** Set a consumer for synthetic scroll events */
     fun setSyntheticScrollConsumer(consumer: Consumer<Float>?)
+
     /** Set a consumer for current gesture overscroll events */
     fun setCurrentGestureOverscrollConsumer(consumer: Consumer<Boolean>?)
+
     /** Set a consumer for heads up height changed events */
     fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
 
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 cf5366b..497ffca 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
@@ -154,6 +154,14 @@
                         }
                     }
 
+                    if (!SceneContainerFlag.isEnabled) {
+                        launch {
+                            // For when the entire view should fade, such as with the brightness
+                            // slider
+                            viewModel.panelAlpha.collect { controller.setMaxAlphaFromView(it) }
+                        }
+                    }
+
                     if (communalHub()) {
                         launch {
                             viewModel.glanceableHubAlpha.collect {
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 e90a64a..ebb0d7d 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
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
@@ -36,7 +37,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
@@ -50,7 +50,7 @@
     dumpManager: DumpManager,
     stackAppearanceInteractor: NotificationStackAppearanceInteractor,
     shadeInteractor: ShadeInteractor,
-    sceneInteractor: SceneInteractor,
+    private val sceneInteractor: SceneInteractor,
     // TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
     // while the flag is off, creating this object too early results in a crash
     keyguardInteractor: Lazy<KeyguardInteractor>,
@@ -63,9 +63,11 @@
     val expandFraction: Flow<Float> =
         combine(
                 shadeInteractor.shadeExpansion,
+                shadeInteractor.shadeMode,
                 shadeInteractor.qsExpansion,
                 sceneInteractor.transitionState,
-            ) { shadeExpansion, qsExpansion, transitionState ->
+                sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
+            ) { shadeExpansion, shadeMode, qsExpansion, transitionState, quickSettingsScene ->
                 when (transitionState) {
                     is ObservableTransitionState.Idle -> {
                         if (transitionState.currentScene == Scenes.Lockscreen) {
@@ -76,16 +78,22 @@
                     }
                     is ObservableTransitionState.Transition -> {
                         if (
-                            (transitionState.fromScene == notificationsScene &&
+                            (transitionState.fromScene in SceneFamilies.NotifShade &&
                                 transitionState.toScene == quickSettingsScene) ||
-                                (transitionState.fromScene == quickSettingsScene &&
-                                    transitionState.toScene == notificationsScene)
+                                (transitionState.fromScene in quickSettingsScene &&
+                                    transitionState.toScene in SceneFamilies.NotifShade) ||
+                                (transitionState.fromScene == Scenes.Lockscreen &&
+                                    transitionState.toScene in SceneFamilies.NotifShade) ||
+                                (transitionState.fromScene in SceneFamilies.NotifShade &&
+                                    transitionState.toScene == Scenes.Lockscreen)
                         ) {
                             1f
                         } else if (
-                            (transitionState.fromScene == Scenes.Gone ||
-                                transitionState.fromScene == Scenes.Lockscreen) &&
-                                transitionState.toScene == quickSettingsScene
+                            shadeMode != ShadeMode.Split &&
+                                (transitionState.fromScene in SceneFamilies.Home &&
+                                    transitionState.toScene == quickSettingsScene) ||
+                                (transitionState.fromScene == quickSettingsScene &&
+                                    transitionState.toScene in SceneFamilies.Home)
                         ) {
                             // during QS expansion, increase fraction at same rate as scrim alpha,
                             // but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN.
@@ -101,6 +109,9 @@
             .distinctUntilChanged()
             .dumpWhileCollecting("expandFraction")
 
+    private operator fun SceneKey.contains(scene: SceneKey) =
+        sceneInteractor.isSceneInFamily(scene, this)
+
     /** The bounds of the notification stack in the current scene. */
     private val shadeScrimClipping: Flow<ShadeScrimClipping?> =
         combine(
@@ -142,6 +153,7 @@
 
     /** Receives the amount (px) that the stack should scroll due to internal expansion. */
     val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll
+
     /**
      * Receives whether the current touch gesture is overscroll as it has already been consumed by
      * the stack.
@@ -150,10 +162,9 @@
         stackAppearanceInteractor::setCurrentGestureOverscroll
 
     /** Whether the notification stack is scrollable or not. */
-    val isScrollable: Flow<Boolean> =
-        sceneInteractor.currentScene
-            .map { it == notificationsScene }
-            .dumpWhileCollecting("isScrollable")
+    val isScrollable: Flow<Boolean> = sceneInteractor.currentScene.map {
+        sceneInteractor.isSceneInFamily(it, SceneFamilies.NotifShade) || it == Scenes.Lockscreen
+    }.dumpWhileCollecting("isScrollable")
 
     /** Whether the notification stack is displayed in doze mode. */
     val isDozing: Flow<Boolean> by lazy {
@@ -163,22 +174,4 @@
             keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing")
         }
     }
-
-    private val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
-
-    private val notificationsScene: SceneKey
-        get() =
-            if (shadeMode.value is ShadeMode.Dual) {
-                Scenes.NotificationsShade
-            } else {
-                Scenes.Shade
-            }
-
-    private val quickSettingsScene: SceneKey
-        get() =
-            if (shadeMode.value is ShadeMode.Dual) {
-                Scenes.QuickSettingsShade
-            } else {
-                Scenes.QuickSettings
-            }
 }
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 6dfaec9..8e58ffb 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
@@ -21,6 +21,7 @@
 
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.common.shared.model.NotificationContainerBounds
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
@@ -53,6 +54,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GoneToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionViewModel
@@ -74,6 +76,7 @@
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import com.android.systemui.util.kotlin.FlowDumperImpl
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -120,6 +123,7 @@
     private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
     private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
     private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel,
+    private val goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
     private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
     private val lockscreenToGlanceableHubTransitionViewModel:
         LockscreenToGlanceableHubTransitionViewModel,
@@ -134,6 +138,7 @@
     private val primaryBouncerToLockscreenTransitionViewModel:
         PrimaryBouncerToLockscreenTransitionViewModel,
     private val aodBurnInViewModel: AodBurnInViewModel,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     unfoldTransitionInteractor: UnfoldTransitionInteractor,
 ) : FlowDumperImpl(dumpManager) {
     private val statesForConstrainedNotifications: Set<KeyguardState> =
@@ -441,7 +446,7 @@
                     anyOf(
                             *toFlowArray(statesForHiddenKeyguard) { state ->
                                 keyguardTransitionInteractor
-                                    .transitionStepsToState(state)
+                                    .transition(Edge.create(to = state))
                                     .map { it.value > 0f && it.transitionState == RUNNING }
                                     .onStart { emit(false) }
                             }
@@ -468,11 +473,27 @@
             }
             .dumpWhileCollecting("isTransitioningToHiddenKeyguard")
 
+    val panelAlpha = keyguardInteractor.panelAlpha
+
+    private fun bouncerToGoneNotificationAlpha(viewState: ViewStateAccessor): Flow<Float> =
+        merge(
+                primaryBouncerToGoneTransitionViewModel.notificationAlpha,
+                alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState),
+            )
+            .sample(communalSceneInteractor.isCommunalVisible) { alpha, isCommunalVisible ->
+                // when glanceable hub is visible, hide notifications during the transition to GONE
+                if (isCommunalVisible) 0f else alpha
+            }
+            .dumpWhileCollecting("bouncerToGoneNotificationAlpha")
+
     fun keyguardAlpha(viewState: ViewStateAccessor): Flow<Float> {
         // All transition view models are mututally exclusive, and safe to merge
         val alphaTransitions =
             merge(
-                alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState),
+                keyguardInteractor.dismissAlpha.dumpWhileCollecting(
+                    "keyguardInteractor.dismissAlpha"
+                ),
+                bouncerToGoneNotificationAlpha(viewState),
                 aodToGoneTransitionViewModel.notificationAlpha(viewState),
                 aodToLockscreenTransitionViewModel.notificationAlpha,
                 aodToOccludedTransitionViewModel.lockscreenAlpha(viewState),
@@ -481,7 +502,8 @@
                 dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
                 goneToAodTransitionViewModel.notificationAlpha,
                 goneToDreamingTransitionViewModel.lockscreenAlpha,
-                goneToDozingTransitionViewModel.lockscreenAlpha,
+                goneToDozingTransitionViewModel.notificationAlpha,
+                goneToLockscreenTransitionViewModel.lockscreenAlpha,
                 lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
                 lockscreenToGoneTransitionViewModel.notificationAlpha(viewState),
                 lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
@@ -489,8 +511,9 @@
                 occludedToAodTransitionViewModel.lockscreenAlpha,
                 occludedToGoneTransitionViewModel.notificationAlpha(viewState),
                 occludedToLockscreenTransitionViewModel.lockscreenAlpha,
-                primaryBouncerToGoneTransitionViewModel.notificationAlpha,
                 primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
+                glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
+                lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
             )
 
         return merge(
@@ -498,24 +521,10 @@
                 // These remaining cases handle alpha changes within an existing state, such as
                 // shade expansion or swipe to dismiss
                 combineTransform(
-                    isOnLockscreenWithoutShade,
                     isTransitioningToHiddenKeyguard,
-                    shadeCollapseFadeIn,
                     alphaForShadeAndQsExpansion,
-                    keyguardInteractor.dismissAlpha.dumpWhileCollecting(
-                        "keyguardInteractor.keyguardAlpha"
-                    ),
-                ) {
-                    isOnLockscreenWithoutShade,
-                    isTransitioningToHiddenKeyguard,
-                    shadeCollapseFadeIn,
-                    alphaForShadeAndQsExpansion,
-                    dismissAlpha ->
-                    if (isOnLockscreenWithoutShade) {
-                        if (!shadeCollapseFadeIn && dismissAlpha != null) {
-                            emit(dismissAlpha)
-                        }
-                    } else if (!isTransitioningToHiddenKeyguard) {
+                ) { isTransitioningToHiddenKeyguard, alphaForShadeAndQsExpansion ->
+                    if (!isTransitioningToHiddenKeyguard) {
                         emit(alphaForShadeAndQsExpansion)
                     }
                 },
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 2ab7aa9..fae0a46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -85,6 +85,26 @@
         )
     }
 
+    override fun startPendingIntentWithoutDismissing(
+        intent: PendingIntent,
+        dismissShade: Boolean,
+        intentSentUiThreadCallback: Runnable?,
+        animationController: ActivityTransitionAnimator.Controller?,
+        fillInIntent: Intent?,
+        extraOptions: Bundle?
+    ) {
+        activityStarterInternal.startPendingIntentDismissingKeyguard(
+            intent = intent,
+            intentSentUiThreadCallback = intentSentUiThreadCallback,
+            animationController = animationController,
+            showOverLockscreen = true,
+            skipLockscreenChecks = true,
+            dismissShade = dismissShade,
+            fillInIntent = fillInIntent,
+            extraOptions = extraOptions,
+        )
+    }
+
     override fun startPendingIntentMaybeDismissingKeyguard(
         intent: PendingIntent,
         intentSentUiThreadCallback: Runnable?,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
index c9becb4..cff9f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
@@ -39,6 +39,7 @@
         associatedView: View? = null,
         animationController: ActivityTransitionAnimator.Controller? = null,
         showOverLockscreen: Boolean = false,
+        skipLockscreenChecks: Boolean = false,
         fillInIntent: Intent? = null,
         extraOptions: Bundle? = null,
     )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index e580f64..dbb95e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -40,6 +40,7 @@
         associatedView: View?,
         animationController: ActivityTransitionAnimator.Controller?,
         showOverLockscreen: Boolean,
+        skipLockscreenChecks: Boolean,
         fillInIntent: Intent?,
         extraOptions: Bundle?
     ) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 2011332..91b5d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -39,7 +39,7 @@
 import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 import com.android.systemui.statusbar.policy.DeviceControlsController;
@@ -430,8 +430,7 @@
 
             boolean isCasting = false;
             for (CastDevice device : mCastController.getCastDevices()) {
-                if (device.state == CastDevice.STATE_CONNECTED
-                        || device.state == CastDevice.STATE_CONNECTING) {
+                if (device.isCasting()) {
                     isCasting = true;
                     break;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index f83aed8..97b6f95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -538,6 +538,9 @@
                     // later to awaken.
                 }
                 mNotificationShadeWindowController.setNotificationShadeFocusable(false);
+                // Notify the interactor first, to prevent race conditions with the screen waking up
+                // that would show a flicker of the lockscreen on DOZING->GONE
+                mBiometricUnlockInteractor.setBiometricUnlockState(mode, biometricUnlockSource);
                 mKeyguardViewMediator.onWakeAndUnlocking(wakeInKeyguard);
                 Trace.endSection();
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 05a4391..d75a738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -261,9 +260,6 @@
 
     boolean isScreenFullyOff();
 
-    @Nullable
-    Intent getEmergencyActionIntent();
-
     boolean isCameraAllowedByAdmin();
 
     boolean isGoingToSleep();
@@ -288,11 +284,12 @@
     void awakenDreams();
 
     /**
-     * Handle a touch event while dreaming when the touch was initiated within a prescribed
-     * swipeable area. This method is provided for cases where swiping in certain areas of a dream
-     * should be handled by CentralSurfaces instead (e.g. swiping communal hub open).
+     * Handle a touch event while dreaming or on the glanceable hub when the touch was initiated
+     * within a prescribed swipeable area. This method is provided for cases where swiping in
+     * certain areas should be handled by CentralSurfaces instead (e.g. swiping hub open, opening
+     * the notification shade over dream or hub).
      */
-    void handleDreamTouch(MotionEvent event);
+    void handleExternalShadeWindowTouch(MotionEvent event);
 
     boolean isBouncerShowing();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 7dac77e..c8a4450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -46,6 +46,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.emergency.EmergencyGesture;
+import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSHost;
@@ -113,6 +115,8 @@
     private int mDisabled1;
     private int mDisabled2;
 
+    private final EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
+
     @Inject
     CentralSurfacesCommandQueueCallbacks(
             CentralSurfaces centralSurfaces,
@@ -143,7 +147,8 @@
             Lazy<CameraLauncher> cameraLauncherLazy,
             UserTracker userTracker,
             QSHost qsHost,
-            ActivityStarter activityStarter) {
+            ActivityStarter activityStarter,
+            EmergencyGestureIntentFactory emergencyGestureIntentFactory) {
         mCentralSurfaces = centralSurfaces;
         mQsController = quickSettingsController;
         mContext = context;
@@ -176,6 +181,7 @@
         mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
                 mVibratorOptional, resources);
         mActivityStarter = activityStarter;
+        mEmergencyGestureIntentFactory = emergencyGestureIntentFactory;
     }
 
     @Override
@@ -395,7 +401,8 @@
 
     @Override
     public void onEmergencyActionLaunchGestureDetected() {
-        Intent emergencyIntent = mCentralSurfaces.getEmergencyActionIntent();
+        Intent emergencyIntent = mEmergencyGestureIntentFactory.invoke(
+                EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
 
         if (emergencyIntent == null) {
             Log.wtf(CentralSurfaces.TAG, "Couldn't find an app to process the emergency intent.");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index a7b5484..3bc8e27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone
 
-import android.content.Intent
 import android.view.MotionEvent
 import androidx.lifecycle.LifecycleRegistry
 import com.android.keyguard.AuthKeyguardMessageArea
@@ -71,7 +70,6 @@
     override fun getNavigationBarView(): NavigationBarView? = null
     override fun setBouncerShowing(bouncerShowing: Boolean) {}
     override fun isScreenFullyOff() = false
-    override fun getEmergencyActionIntent(): Intent? = null
     override fun isCameraAllowedByAdmin() = false
     override fun isGoingToSleep() = false
     override fun notifyBiometricAuthModeChanged() {}
@@ -80,7 +78,7 @@
     override fun updateScrimController() {}
     override fun shouldIgnoreTouch() = false
     override fun isDeviceInteractive() = false
-    override fun handleDreamTouch(event: MotionEvent?) {}
+    override fun handleExternalShadeWindowTouch(event: MotionEvent?) {}
     override fun handleCommunalHubTouch(event: MotionEvent?) {}
     override fun awakenDreams() {}
     override fun isBouncerShowing() = false
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 7567f36..62c139b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -49,12 +49,9 @@
 import android.app.WallpaperManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.hardware.devicestate.DeviceStateManager;
@@ -74,7 +71,6 @@
 import android.provider.Settings;
 import android.service.dreams.IDreamManager;
 import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -136,6 +132,7 @@
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.emergency.EmergencyGesture;
+import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.fragments.ExtensionFragmentListener;
@@ -247,7 +244,6 @@
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.Executor;
@@ -598,6 +594,8 @@
     private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor;
     private final GlanceableHubContainerController mGlanceableHubContainerController;
 
+    private final EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
+
     /**
      * Public constructor for CentralSurfaces.
      *
@@ -710,7 +708,8 @@
             Provider<FingerprintManager> fingerprintManager,
             ActivityStarter activityStarter,
             BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor,
-            GlanceableHubContainerController glanceableHubContainerController
+            GlanceableHubContainerController glanceableHubContainerController,
+            EmergencyGestureIntentFactory emergencyGestureIntentFactory
     ) {
         mContext = context;
         mNotificationsController = notificationsController;
@@ -806,6 +805,7 @@
         mActivityStarter = activityStarter;
         mBrightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor;
         mGlanceableHubContainerController = glanceableHubContainerController;
+        mEmergencyGestureIntentFactory = emergencyGestureIntentFactory;
 
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         mStartingSurfaceOptional = startingSurfaceOptional;
@@ -2628,7 +2628,8 @@
             }
             if (mLaunchEmergencyActionWhenFinishedWaking) {
                 mLaunchEmergencyActionWhenFinishedWaking = false;
-                Intent emergencyIntent = getEmergencyActionIntent();
+                Intent emergencyIntent = mEmergencyGestureIntentFactory.invoke(
+                        EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
                 if (emergencyIntent != null) {
                     mContext.startActivityAsUser(emergencyIntent,
                             getActivityUserHandle(emergencyIntent));
@@ -2694,52 +2695,6 @@
         return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
     }
 
-    @Nullable
-    @Override
-    public Intent getEmergencyActionIntent() {
-        Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
-        PackageManager pm = mContext.getPackageManager();
-        List<ResolveInfo> emergencyActivities = pm.queryIntentActivities(emergencyIntent,
-                PackageManager.MATCH_SYSTEM_ONLY);
-        ResolveInfo resolveInfo = getTopEmergencySosInfo(emergencyActivities);
-        if (resolveInfo == null) {
-            Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
-            return null;
-        }
-        emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
-                resolveInfo.activityInfo.name));
-        emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return emergencyIntent;
-    }
-
-    /**
-     * Select and return the "best" ResolveInfo for Emergency SOS Activity.
-     */
-    private @Nullable ResolveInfo getTopEmergencySosInfo(List<ResolveInfo> emergencyActivities) {
-        // No matched activity.
-        if (emergencyActivities == null || emergencyActivities.isEmpty()) {
-            return null;
-        }
-
-        // Of multiple matched Activities, give preference to the pre-set package name.
-        String preferredAppPackageName =
-                mContext.getString(R.string.config_preferredEmergencySosPackage);
-
-        // If there is no preferred app, then return first match.
-        if (TextUtils.isEmpty(preferredAppPackageName)) {
-            return emergencyActivities.get(0);
-        }
-
-        for (ResolveInfo emergencyInfo: emergencyActivities) {
-            // If activity is from the preferred app, use it.
-            if (TextUtils.equals(emergencyInfo.activityInfo.packageName, preferredAppPackageName)) {
-                return emergencyInfo;
-            }
-        }
-        // No matching activity: return first match
-        return emergencyActivities.get(0);
-    }
-
     @Override
     public boolean isCameraAllowedByAdmin() {
         if (mDevicePolicyManager.getCameraDisabled(null,
@@ -2954,8 +2909,8 @@
     };
 
     @Override
-    public void handleDreamTouch(MotionEvent event) {
-        getNotificationShadeWindowViewController().handleDreamTouch(event);
+    public void handleExternalShadeWindowTouch(MotionEvent event) {
+        getNotificationShadeWindowViewController().handleExternalTouch(event);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index cff46ab..0ba4aab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -223,7 +223,8 @@
             }
             return;
         }
-        StatusBarIcon icon = new StatusBarIcon(iconPkg, UserHandle.SYSTEM, iconId, 0, 0, "Demo");
+        StatusBarIcon icon = new StatusBarIcon(iconPkg, UserHandle.SYSTEM, iconId, 0, 0, "Demo",
+                StatusBarIcon.Type.SystemIcon);
         icon.visible = true;
         StatusBarIconView v = new StatusBarIconView(getContext(), slot, null, false);
         v.setTag(slot);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4bf122d..3925beb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -202,6 +202,7 @@
      * Gets the touchable region needed for heads up notifications. Returns null if no touchable
      * region is required (ie: no heads up notification currently exists).
      */
+    // TODO(b/347007367): With scene container enabled this method may report outdated regions
     @Override
     public @Nullable Region getTouchableRegion() {
         NotificationEntry topEntry = getTopEntry();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index e44edcb..cd59d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -21,6 +21,7 @@
 
 import static com.android.systemui.Flags.updateUserSwitcherBackground;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -46,6 +47,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.log.core.LogLevel;
@@ -83,6 +85,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 import javax.inject.Inject;
 
@@ -128,6 +131,7 @@
     private final Executor mBackgroundExecutor;
     private final Object mLock = new Object();
     private final KeyguardLogger mLogger;
+    private final CommunalSceneInteractor mCommunalSceneInteractor;
 
     private View mSystemIconsContainer;
     private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
@@ -241,6 +245,12 @@
                 }
             };
 
+    private boolean mCommunalShowing;
+
+    private final Consumer<Boolean> mCommunalConsumer = (communalShowing) -> {
+        mCommunalShowing = communalShowing;
+        updateViewState();
+    };
 
     private final DisableStateTracker mDisableStateTracker;
 
@@ -298,7 +308,8 @@
             @Main Executor mainExecutor,
             @Background Executor backgroundExecutor,
             KeyguardLogger logger,
-            StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory
+            StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory,
+            CommunalSceneInteractor communalSceneInteractor
     ) {
         super(view);
         mCarrierTextController = carrierTextController;
@@ -324,6 +335,7 @@
         mMainExecutor = mainExecutor;
         mBackgroundExecutor = backgroundExecutor;
         mLogger = logger;
+        mCommunalSceneInteractor = communalSceneInteractor;
 
         mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
         mKeyguardStateController.addCallback(
@@ -405,6 +417,7 @@
                 UserHandle.USER_ALL);
         updateUserSwitcher();
         onThemeChanged();
+        collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer);
     }
 
     @Override
@@ -559,6 +572,7 @@
                         && !mDozing
                         && !hideForBypass
                         && !mDisableStateTracker.isDisabled()
+                        && !mCommunalShowing
                         ? View.VISIBLE : View.INVISIBLE;
 
         updateViewState(newAlpha, newVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index fc29eab..639560f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -228,6 +228,7 @@
         associatedView: View?,
         animationController: ActivityTransitionAnimator.Controller?,
         showOverLockscreen: Boolean,
+        skipLockscreenChecks: Boolean,
         fillInIntent: Intent?,
         extraOptions: Bundle?,
     ) {
@@ -246,10 +247,11 @@
         val actuallyShowOverLockscreen =
             showOverLockscreen &&
                 intent.isActivity &&
-                activityIntentHelper.wouldPendingShowOverLockscreen(
-                    intent,
-                    lockScreenUserManager.currentUserId
-                )
+                (skipLockscreenChecks ||
+                    activityIntentHelper.wouldPendingShowOverLockscreen(
+                        intent,
+                        lockScreenUserManager.currentUserId
+                    ))
 
         val animate =
             !willLaunchResolverActivity &&
@@ -469,12 +471,16 @@
                         shadeControllerLazy.get().collapseShadeForActivityStart()
                     }
                     if (communalHub()) {
-                        communalSceneInteractor.snapToScene(CommunalScenes.Blank)
+                        communalSceneInteractor.changeSceneForActivityStartOnDismissKeyguard()
                     }
                     return deferred
                 }
 
                 override fun willRunAnimationOnKeyguard(): Boolean {
+                    if (communalHub() && communalSceneInteractor.isIdleOnCommunal.value) {
+                        // Override to false when launching activity over the hub that requires auth
+                        return false
+                    }
                     return willAnimateOnKeyguard
                 }
             }
@@ -555,7 +561,12 @@
 
                 override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
                     super.onTransitionAnimationStart(isExpandingFullyAbove)
-
+                    if (communalHub()) {
+                        communalSceneInteractor.snapToScene(
+                            CommunalScenes.Blank,
+                            ActivityTransitionAnimator.TIMINGS.totalDuration
+                        )
+                    }
                     // Double check that the keyguard is still showing and not going
                     // away, but if so set the keyguard occluded. Typically, WM will let
                     // KeyguardViewMediator know directly, but we're overriding that to
@@ -581,9 +592,6 @@
                     // collapse the shade (or at least run the post collapse runnables)
                     // later on.
                     centralSurfaces?.setIsLaunchingActivityOverLockscreen(false, false)
-                    if (communalHub()) {
-                        communalSceneInteractor.snapToScene(CommunalScenes.Blank)
-                    }
                     delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
index 2235035..d2a1c44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java
@@ -29,6 +29,7 @@
 import com.android.app.animation.Interpolators;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
@@ -124,6 +125,7 @@
     @AssistedInject
     public LightBarTransitionsController(
             Context context,
+            @Background Handler bgHandler,
             @Assisted DarkIntensityApplier applier,
             CommandQueue commandQueue,
             KeyguardStateController keyguardStateController,
@@ -140,7 +142,7 @@
         mContext = context;
         mDisplayId = mContext.getDisplayId();
         mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
-                mHandler, mContext, this::onNavigationSettingsChanged);
+                mHandler, bgHandler, mContext, this::onNavigationSettingsChanged);
         mGestureNavigationSettingsObserver.register();
         onNavigationSettingsChanged();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 3784132..2371eed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -62,7 +62,7 @@
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -521,8 +521,7 @@
     private void updateCast() {
         boolean isCasting = false;
         for (CastDevice device : mCast.getCastDevices()) {
-            if (device.state == CastDevice.STATE_CONNECTING
-                    || device.state == CastDevice.STATE_CONNECTED) {
+            if (device.isCasting()) {
                 isCasting = true;
                 break;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
index 08a890d..d699b38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
@@ -155,6 +155,7 @@
                     0,
                     0,
                     contentDescription,
+                    StatusBarIcon.Type.SystemIcon,
                 )
             holder.tag = state.subId
             return holder
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index db4f0af..b40bf56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -73,6 +73,7 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
 import com.android.systemui.keyguard.shared.model.DismissAction;
+import com.android.systemui.keyguard.shared.model.Edge;
 import com.android.systemui.keyguard.shared.model.KeyguardDone;
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -508,8 +509,8 @@
         mListenForCanShowAlternateBouncer = null;
         if (!DeviceEntryUdfpsRefactor.isEnabled()) {
             mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow(
-                    mKeyguardTransitionInteractor.transitionStepsFromState(
-                            KeyguardState.ALTERNATE_BOUNCER),
+                    mKeyguardTransitionInteractor
+                            .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)),
                     this::consumeFromAlternateBouncerTransitionSteps
             );
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index d1189e1..4b1ee58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -29,6 +29,7 @@
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
 import android.view.WindowInsets;
 
+import com.android.compose.animation.scene.ObservableTransitionState;
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.ScreenDecorations;
@@ -38,7 +39,7 @@
 import com.android.systemui.res.R;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.scene.shared.model.Scenes;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -67,7 +68,7 @@
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
 
     private boolean mIsStatusBarExpanded = false;
-    private boolean mIsSceneContainerVisible = false;
+    private boolean mIsIdleOnGone = false;
     private boolean mShouldAdjustInsets = false;
     private View mNotificationShadeWindowView;
     private View mNotificationPanelView;
@@ -87,7 +88,6 @@
             NotificationShadeWindowController notificationShadeWindowController,
             ConfigurationController configurationController,
             HeadsUpManager headsUpManager,
-            ShadeExpansionStateManager shadeExpansionStateManager,
             ShadeInteractor shadeInteractor,
             Provider<SceneInteractor> sceneInteractor,
             JavaAdapter javaAdapter,
@@ -131,8 +131,8 @@
 
         if (SceneContainerFlag.isEnabled()) {
             javaAdapter.alwaysCollectFlow(
-                    sceneInteractor.get().isVisible(),
-                    this::onSceneContainerVisibilityChanged);
+                    sceneInteractor.get().getTransitionState(),
+                    this::onSceneChanged);
         } else {
             javaAdapter.alwaysCollectFlow(
                     shadeInteractor.isAnyExpanded(),
@@ -167,10 +167,11 @@
         }
     }
 
-    private void onSceneContainerVisibilityChanged(Boolean isVisible) {
-        if (isVisible != mIsSceneContainerVisible) {
-            mIsSceneContainerVisible = isVisible;
-            if (isVisible) {
+    private void onSceneChanged(ObservableTransitionState transitionState) {
+        boolean isIdleOnGone = transitionState.isIdle(Scenes.Gone);
+        if (isIdleOnGone != mIsIdleOnGone) {
+            mIsIdleOnGone = isIdleOnGone;
+            if (!isIdleOnGone) {
                 // make sure our state is sensible
                 mForceCollapsedUntilLayout = false;
             }
@@ -281,7 +282,7 @@
         // since we don't want stray touches to go through the light reveal scrim to whatever is
         // underneath.
         return mIsStatusBarExpanded
-                || mIsSceneContainerVisible
+                || !mIsIdleOnGone
                 || mPrimaryBouncerInteractor.isShowing().getValue()
                 || mAlternateBouncerInteractor.isVisibleState()
                 || mUnlockedScreenOffAnimationController.isAnimationPlaying();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index a7d4ce3..9f98b54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -29,35 +29,37 @@
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.CoreStartable
 import com.android.systemui.Dumpable
-import com.android.systemui.res.R
+import com.android.systemui.Flags
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.time.SystemClock
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
 import java.io.PrintWriter
 import java.util.concurrent.Executor
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
-/**
- * A controller to handle the ongoing call chip in the collapsed status bar.
- */
+/** A controller to handle the ongoing call chip in the collapsed status bar. */
 @SysUISingleton
-class OngoingCallController @Inject constructor(
+class OngoingCallController
+@Inject
+constructor(
     @Application private val scope: CoroutineScope,
     private val context: Context,
     private val ongoingCallRepository: OngoingCallRepository,
@@ -79,54 +81,61 @@
 
     private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
     private val uidObserver = CallAppUidObserver()
-    private val notifListener = object : NotifCollectionListener {
-        // Temporary workaround for b/178406514 for testing purposes.
-        //
-        // b/178406514 means that posting an incoming call notif then updating it to an ongoing call
-        // notif does not work (SysUI never receives the update). This workaround allows us to
-        // trigger the ongoing call chip when an ongoing call notif is *added* rather than
-        // *updated*, allowing us to test the chip.
-        //
-        // TODO(b/183229367): Remove this function override when b/178406514 is fixed.
-        override fun onEntryAdded(entry: NotificationEntry) {
-            onEntryUpdated(entry, true)
-        }
+    private val notifListener =
+        object : NotifCollectionListener {
+            // Temporary workaround for b/178406514 for testing purposes.
+            //
+            // b/178406514 means that posting an incoming call notif then updating it to an ongoing
+            // call notif does not work (SysUI never receives the update). This workaround allows us
+            // to trigger the ongoing call chip when an ongoing call notif is *added* rather than
+            // *updated*, allowing us to test the chip.
+            //
+            // TODO(b/183229367): Remove this function override when b/178406514 is fixed.
+            override fun onEntryAdded(entry: NotificationEntry) {
+                onEntryUpdated(entry, true)
+            }
 
-        override fun onEntryUpdated(entry: NotificationEntry) {
-            // We have a new call notification or our existing call notification has been updated.
-            // TODO(b/183229367): This likely won't work if you take a call from one app then
-            //  switch to a call from another app.
-            if (callNotificationInfo == null && isCallNotification(entry) ||
-                    (entry.sbn.key == callNotificationInfo?.key)) {
-                val newOngoingCallInfo = CallNotificationInfo(
-                        entry.sbn.key,
-                        entry.sbn.notification.getWhen(),
-                        entry.sbn.notification.contentIntent,
-                        entry.sbn.uid,
-                        entry.sbn.notification.extras.getInt(
-                                Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING,
-                        statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
-                )
-                if (newOngoingCallInfo == callNotificationInfo) {
-                    return
+            override fun onEntryUpdated(entry: NotificationEntry) {
+                // We have a new call notification or our existing call notification has been
+                // updated.
+                // TODO(b/183229367): This likely won't work if you take a call from one app then
+                //  switch to a call from another app.
+                if (
+                    callNotificationInfo == null && isCallNotification(entry) ||
+                        (entry.sbn.key == callNotificationInfo?.key)
+                ) {
+                    val newOngoingCallInfo =
+                        CallNotificationInfo(
+                            entry.sbn.key,
+                            entry.sbn.notification.getWhen(),
+                            entry.sbn.notification.contentIntent,
+                            entry.sbn.uid,
+                            entry.sbn.notification.extras.getInt(
+                                Notification.EXTRA_CALL_TYPE,
+                                -1
+                            ) == CALL_TYPE_ONGOING,
+                            statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false
+                        )
+                    if (newOngoingCallInfo == callNotificationInfo) {
+                        return
+                    }
+
+                    callNotificationInfo = newOngoingCallInfo
+                    if (newOngoingCallInfo.isOngoing) {
+                        updateChip()
+                    } else {
+                        removeChip()
+                    }
                 }
+            }
 
-                callNotificationInfo = newOngoingCallInfo
-                if (newOngoingCallInfo.isOngoing) {
-                    updateChip()
-                } else {
+            override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+                if (entry.sbn.key == callNotificationInfo?.key) {
                     removeChip()
                 }
             }
         }
 
-        override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
-            if (entry.sbn.key == callNotificationInfo?.key) {
-                removeChip()
-            }
-        }
-    }
-
     override fun start() {
         dumpManager.registerDumpable(this)
         notifCollection.addCollectionListener(notifListener)
@@ -169,8 +178,24 @@
      */
     fun hasOngoingCall(): Boolean {
         return callNotificationInfo?.isOngoing == true &&
-                // When the user is in the phone app, don't show the chip.
-                !uidObserver.isCallAppVisible
+            // When the user is in the phone app, don't show the chip.
+            !uidObserver.isCallAppVisible
+    }
+
+    /** Creates the right [OngoingCallModel] based on the call state. */
+    private fun getOngoingCallModel(): OngoingCallModel {
+        if (hasOngoingCall()) {
+            val currentInfo =
+                callNotificationInfo
+                    // This shouldn't happen, but protect against it in case
+                    ?: return OngoingCallModel.NoCall
+            return OngoingCallModel.InCall(
+                startTimeMs = currentInfo.callStartTime,
+                intent = currentInfo.intent,
+            )
+        } else {
+            return OngoingCallModel.NoCall
+        }
     }
 
     override fun addCallback(listener: OngoingCallListener) {
@@ -182,9 +207,7 @@
     }
 
     override fun removeCallback(listener: OngoingCallListener) {
-        synchronized(mListeners) {
-            mListeners.remove(listener)
-        }
+        synchronized(mListeners) { mListeners.remove(listener) }
     }
 
     private fun updateChip() {
@@ -194,18 +217,29 @@
         val timeView = currentChipView?.getTimeView()
 
         if (currentChipView != null && timeView != null) {
-            if (currentCallNotificationInfo.hasValidStartTime()) {
-                timeView.setShouldHideText(false)
-                timeView.base = currentCallNotificationInfo.callStartTime -
-                        systemClock.currentTimeMillis() +
-                        systemClock.elapsedRealtime()
-                timeView.start()
-            } else {
-                timeView.setShouldHideText(true)
-                timeView.stop()
+            if (!Flags.statusBarScreenSharingChips()) {
+                // If the new status bar screen sharing chips are enabled, then the display logic
+                // for *all* status bar chips (both the call chip and the screen sharing chips) are
+                // handled by CollapsedStatusBarViewBinder, *not* this class. We need to disable
+                // this class from making any display changes because the new chips use the same
+                // view as the old call chip.
+                // TODO(b/332662551): We should move this whole controller class to recommended
+                // architecture so that we don't need to awkwardly disable only some parts of this
+                // class.
+                if (currentCallNotificationInfo.hasValidStartTime()) {
+                    timeView.setShouldHideText(false)
+                    timeView.base =
+                        currentCallNotificationInfo.callStartTime -
+                            systemClock.currentTimeMillis() + systemClock.elapsedRealtime()
+                    timeView.start()
+                } else {
+                    timeView.setShouldHideText(true)
+                    timeView.stop()
+                }
+                updateChipClickListener()
             }
-            updateChipClickListener()
 
+            // But, this class still needs to do the non-display logic regardless of the flag.
             uidObserver.registerWithUid(currentCallNotificationInfo.uid)
             if (!currentCallNotificationInfo.statusBarSwipedAway) {
                 statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(true)
@@ -218,14 +252,23 @@
             callNotificationInfo = null
 
             if (DEBUG) {
-                Log.w(TAG, "Ongoing call chip view could not be found; " +
-                        "Not displaying chip in status bar")
+                Log.w(
+                    TAG,
+                    "Ongoing call chip view could not be found; " +
+                        "Not displaying chip in status bar"
+                )
             }
         }
     }
 
     private fun updateChipClickListener() {
-        if (callNotificationInfo == null) { return }
+        if (Flags.statusBarScreenSharingChips()) {
+            return
+        }
+
+        if (callNotificationInfo == null) {
+            return
+        }
         val currentChipView = chipView
         val backgroundView =
             currentChipView?.findViewById<View>(R.id.ongoing_activity_chip_background)
@@ -237,7 +280,8 @@
                     intent,
                     ActivityTransitionAnimator.Controller.fromView(
                         backgroundView,
-                        InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+                        InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+                    )
                 )
             }
         }
@@ -249,9 +293,11 @@
     }
 
     private fun updateGestureListening() {
-        if (callNotificationInfo == null ||
-            callNotificationInfo?.statusBarSwipedAway == true ||
-            !isFullscreen) {
+        if (
+            callNotificationInfo == null ||
+                callNotificationInfo?.statusBarSwipedAway == true ||
+                !isFullscreen
+        ) {
             swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
         } else {
             swipeStatusBarAwayGestureHandler.addOnGestureDetectedCallback(TAG) { _ ->
@@ -262,7 +308,9 @@
 
     private fun removeChip() {
         callNotificationInfo = null
-        tearDownChipView()
+        if (!Flags.statusBarScreenSharingChips()) {
+            tearDownChipView()
+        }
         statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
         swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
         sendStateChangeEvent()
@@ -270,30 +318,31 @@
     }
 
     /** Tear down anything related to the chip view to prevent leaks. */
-    @VisibleForTesting
-    fun tearDownChipView() = chipView?.getTimeView()?.stop()
+    @VisibleForTesting fun tearDownChipView() = chipView?.getTimeView()?.stop()
 
     private fun View.getTimeView(): ChipChronometer? {
         return this.findViewById(R.id.ongoing_activity_chip_time)
     }
 
     /**
-    * If there's an active ongoing call, then we will force the status bar to always show, even if
-    * the user is in immersive mode. However, we also want to give users the ability to swipe away
-    * the status bar if they need to access the area under the status bar.
-    *
-    * This method updates the status bar window appropriately when the swipe away gesture is
-    * detected.
-    */
+     * If there's an active ongoing call, then we will force the status bar to always show, even if
+     * the user is in immersive mode. However, we also want to give users the ability to swipe away
+     * the status bar if they need to access the area under the status bar.
+     *
+     * This method updates the status bar window appropriately when the swipe away gesture is
+     * detected.
+     */
     private fun onSwipeAwayGestureDetected() {
-        if (DEBUG) { Log.d(TAG, "Swipe away gesture detected") }
+        if (DEBUG) {
+            Log.d(TAG, "Swipe away gesture detected")
+        }
         callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true)
         statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false)
         swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
     }
 
     private fun sendStateChangeEvent() {
-        ongoingCallRepository.setHasOngoingCall(hasOngoingCall())
+        ongoingCallRepository.setOngoingCallState(getOngoingCallModel())
         mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
     }
 
@@ -308,8 +357,8 @@
         val statusBarSwipedAway: Boolean
     ) {
         /**
-         * Returns true if the notification information has a valid call start time.
-         * See b/192379214.
+         * Returns true if the notification information has a valid call start time. See
+         * b/192379214.
          */
         fun hasValidStartTime(): Boolean = callStartTime > 0
     }
@@ -342,9 +391,10 @@
             callAppUid = uid
 
             try {
-                isCallAppVisible = isProcessVisibleToUser(
-                    iActivityManager.getUidProcessState(uid, context.opPackageName)
-                )
+                isCallAppVisible =
+                    isProcessVisibleToUser(
+                        iActivityManager.getUidProcessState(uid, context.opPackageName)
+                    )
                 if (isRegistered) {
                     return
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index 886481e..9317ebe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone.ongoingcall.data.repository
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -32,15 +33,15 @@
  */
 @SysUISingleton
 class OngoingCallRepository @Inject constructor() {
-    private val _hasOngoingCall = MutableStateFlow(false)
-    /** True if there's currently an ongoing call notification and false otherwise. */
-    val hasOngoingCall: StateFlow<Boolean> = _hasOngoingCall.asStateFlow()
+    private val _ongoingCallState = MutableStateFlow<OngoingCallModel>(OngoingCallModel.NoCall)
+    /** The current ongoing call state. */
+    val ongoingCallState: StateFlow<OngoingCallModel> = _ongoingCallState.asStateFlow()
 
     /**
-     * Sets whether there's currently an ongoing call notification. Should only be set from
+     * Sets the current ongoing call state, based on notifications. Should only be set from
      * [com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController].
      */
-    fun setHasOngoingCall(hasOngoingCall: Boolean) {
-        _hasOngoingCall.value = hasOngoingCall
+    fun setOngoingCallState(state: OngoingCallModel) {
+        _ongoingCallState.value = state
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
new file mode 100644
index 0000000..50649a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.phone.ongoingcall.shared.model
+
+import android.app.PendingIntent
+
+/** Represents the state of any ongoing calls. */
+sealed interface OngoingCallModel {
+    /** There is no ongoing call. */
+    data object NoCall : OngoingCallModel
+
+    /**
+     * There *is* an ongoing call.
+     *
+     * @property startTimeMs the time that the phone call started, based on the notification's
+     *   `when` field. Importantly, this time is relative to
+     *   [com.android.systemui.util.time.SystemClock.currentTimeMillis], **not**
+     *   [com.android.systemui.util.time.SystemClock.elapsedRealtime].
+     * @property intent the intent associated with the call notification.
+     */
+    data class InCall(val startTimeMs: Long, val intent: PendingIntent?) : OngoingCallModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
index fabf858d..85213cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
@@ -227,8 +227,8 @@
         StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0);
         if (holder == null) {
             StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
-                    Icon.createWithResource(
-                            mContext, resourceId), 0, 0, contentDescription);
+                    Icon.createWithResource(mContext, resourceId), 0, 0,
+                    contentDescription, StatusBarIcon.Type.SystemIcon);
             holder = StatusBarIconHolder.fromIcon(icon);
             setIcon(slot, holder);
         } else {
@@ -295,7 +295,7 @@
                 } else {
                     holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
                             Icon.createWithResource(mContext, state.callStrengthResId), 0, 0,
-                            state.callStrengthDescription));
+                            state.callStrengthDescription, StatusBarIcon.Type.SystemIcon));
                 }
                 setIcon(slot, holder);
             }
@@ -320,7 +320,7 @@
                 } else {
                     holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
                             Icon.createWithResource(mContext, state.noCallingResId), 0, 0,
-                            state.noCallingDescription));
+                            state.noCallingDescription, StatusBarIcon.Type.SystemIcon));
                 }
                 setIcon(slot, holder);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
index f4e3eab..0871c86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt
@@ -18,6 +18,7 @@
 
 import android.os.PersistableBundle
 import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL
 import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
 import androidx.annotation.VisibleForTesting
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -66,10 +67,16 @@
     /** Flow tracking the [KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL] config */
     val showOperatorNameInStatusBar: StateFlow<Boolean> = showOperatorName.config
 
+    private val showNetworkSlice =
+        BooleanCarrierConfig(KEY_SHOW_5G_SLICE_ICON_BOOL, defaultConfig)
+    /** Flow tracking the [KEY_SHOW_5G_SLICE_ICON_BOOL] config */
+    val allowNetworkSliceIndicator: StateFlow<Boolean> = showNetworkSlice.config
+
     private val trackedConfigs =
         listOf(
             inflateSignalStrength,
             showOperatorName,
+            showNetworkSlice,
         )
 
     /** Ingest a new carrier config, and switch all of the tracked keys over to the new values */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 425c58b..205205e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -48,6 +48,9 @@
     /** Reflects the value from the carrier config INFLATE_SIGNAL_STRENGTH for this connection */
     val inflateSignalStrength: StateFlow<Boolean>
 
+    /** Carrier config KEY_SHOW_5G_SLICE_ICON_BOOL for this connection */
+    val allowNetworkSliceIndicator: StateFlow<Boolean>
+
     /**
      * The table log buffer created for this connection. Will have the name "MobileConnectionLog
      * [subId]"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
index 83d5f2b..3261b71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt
@@ -79,6 +79,9 @@
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), _inflateSignalStrength.value)
 
+    // I don't see a reason why we would turn the config off for demo mode.
+    override val allowNetworkSliceIndicator = MutableStateFlow(true)
+
     private val _isEmergencyOnly = MutableStateFlow(false)
     override val isEmergencyOnly =
         _isEmergencyOnly
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index a532e62..2e47678 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -166,6 +166,7 @@
     override val isRoaming = MutableStateFlow(false).asStateFlow()
     override val carrierId = MutableStateFlow(INVALID_SUBSCRIPTION_ID).asStateFlow()
     override val inflateSignalStrength = MutableStateFlow(false).asStateFlow()
+    override val allowNetworkSliceIndicator = MutableStateFlow(false).asStateFlow()
     override val isEmergencyOnly = MutableStateFlow(false).asStateFlow()
     override val operatorAlphaShort = MutableStateFlow(null).asStateFlow()
     override val isInService = MutableStateFlow(true).asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index 41559b2..a5e47a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -308,6 +308,21 @@
                 activeRepo.value.inflateSignalStrength.value
             )
 
+    override val allowNetworkSliceIndicator =
+        activeRepo
+            .flatMapLatest { it.allowNetworkSliceIndicator }
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                columnName = "allowSlice",
+                initialValue = activeRepo.value.allowNetworkSliceIndicator.value,
+            )
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                activeRepo.value.allowNetworkSliceIndicator.value
+            )
+
     override val numberOfLevels =
         activeRepo
             .flatMapLatest { it.numberOfLevels }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 6803a9d..9449659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -310,6 +310,7 @@
             .stateIn(scope, SharingStarted.WhileSubscribed(), UnknownNetworkType)
 
     override val inflateSignalStrength = systemUiCarrierConfig.shouldInflateSignalStrength
+    override val allowNetworkSliceIndicator = systemUiCarrierConfig.allowNetworkSliceIndicator
 
     override val numberOfLevels =
         inflateSignalStrength
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 ed9e405..507759c 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
@@ -253,7 +253,13 @@
             )
 
     override val showSliceAttribution: StateFlow<Boolean> =
-        connectionRepository.hasPrioritizedNetworkCapabilities
+        combine(
+                connectionRepository.allowNetworkSliceIndicator,
+                connectionRepository.hasPrioritizedNetworkCapabilities,
+            ) { allowed, hasPrioritizedNetworkCapabilities ->
+                allowed && hasPrioritizedNetworkCapabilities
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val isNonTerrestrial: StateFlow<Boolean> =
         if (Flags.carrierEnabledSatelliteFlag()) {
@@ -350,7 +356,8 @@
         shownLevel.map {
             SignalIconModel.Satellite(
                 level = it,
-                icon = SatelliteIconModel.fromSignalStrength(it)
+                icon =
+                    SatelliteIconModel.fromSignalStrength(it)
                         ?: SatelliteIconModel.fromSignalStrength(0)!!
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
index d38e834..1d08f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepository.kt
@@ -25,6 +25,9 @@
  * given mobile data subscription.
  */
 interface DeviceBasedSatelliteRepository {
+    /** The current status of satellite provisioning. If not false, we don't want to show an icon */
+    val isSatelliteProvisioned: StateFlow<Boolean>
+
     /** See [SatelliteConnectionState] for available states */
     val connectionState: StateFlow<SatelliteConnectionState>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
index 6b1bc65..58c30e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcher.kt
@@ -97,6 +97,11 @@
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl)
 
+    override val isSatelliteProvisioned: StateFlow<Boolean> =
+        activeRepo
+            .flatMapLatest { it.isSatelliteProvisioned }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isSatelliteProvisioned.value)
+
     override val connectionState: StateFlow<SatelliteConnectionState> =
         activeRepo
             .flatMapLatest { it.connectionState }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
index 56034f0..6ad295e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/demo/DemoDeviceBasedSatelliteRepository.kt
@@ -36,6 +36,7 @@
 ) : DeviceBasedSatelliteRepository {
     private var demoCommandJob: Job? = null
 
+    override val isSatelliteProvisioned = MutableStateFlow(true)
     override val connectionState = MutableStateFlow(SatelliteConnectionState.Unknown)
     override val signalStrength = MutableStateFlow(0)
     override val isSatelliteAllowedForCurrentLocation = MutableStateFlow(true)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 1449e53..ec3af87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -23,6 +23,7 @@
 import android.telephony.satellite.SatelliteManager
 import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
 import android.telephony.satellite.SatelliteModemStateCallback
+import android.telephony.satellite.SatelliteProvisionStateCallback
 import android.telephony.satellite.SatelliteSupportedStateCallback
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -337,6 +338,43 @@
             }
         }
 
+    override val isSatelliteProvisioned: StateFlow<Boolean> =
+        satelliteSupport
+            .whenSupported(
+                supported = ::satelliteProvisioned,
+                orElse = flowOf(false),
+                retrySignal = telephonyProcessCrashedEvent,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    private fun satelliteProvisioned(sm: SupportedSatelliteManager): Flow<Boolean> =
+        conflatedCallbackFlow {
+            val callback = SatelliteProvisionStateCallback { provisioned ->
+                logBuffer.i {
+                    "onSatelliteProvisionStateChanged: " +
+                        if (provisioned) "provisioned" else "not provisioned"
+                }
+                trySend(provisioned)
+            }
+
+            var registered = false
+            try {
+                sm.registerForProvisionStateChanged(
+                    bgDispatcher.asExecutor(),
+                    callback,
+                )
+                registered = true
+            } catch (e: Exception) {
+                logBuffer.e("error registering for provisioning state callback", e)
+            }
+
+            awaitClose {
+                if (registered) {
+                    sm.unregisterForProvisionStateChanged(callback)
+                }
+            }
+        }
+
     /**
      * Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there
      * are active listeners to [isSatelliteAllowedForCurrentLocation]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index b66ace6..03f88c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
 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.policy.domain.interactor.DeviceProvisioningInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,7 +44,6 @@
 constructor(
     val repo: DeviceBasedSatelliteRepository,
     iconsInteractor: MobileIconsInteractor,
-    deviceProvisioningInteractor: DeviceProvisioningInteractor,
     wifiInteractor: WifiInteractor,
     @Application scope: CoroutineScope,
     @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
@@ -78,7 +76,7 @@
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
 
-    val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned
+    val isSatelliteProvisioned = repo.isSatelliteProvisioned
 
     val isWifiActive: Flow<Boolean> =
         wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 0ed1b9b..48278d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -79,11 +79,11 @@
             } else {
                 combine(
                     interactor.isSatelliteAllowed,
-                    interactor.isDeviceProvisioned,
+                    interactor.isSatelliteProvisioned,
                     interactor.isWifiActive,
                     airplaneModeRepository.isAirplaneMode
-                ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode ->
-                    isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode
+                ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode ->
+                    isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode
                 }
             }
         }
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 a2ec1f2..7644653 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
@@ -27,8 +27,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
@@ -94,7 +94,7 @@
                                 is OngoingActivityChipModel.Shown -> {
                                     IconViewBinder.bind(chipModel.icon, chipIconView)
                                     ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
-                                    // TODO(b/332662551): Attach click listener to chip
+                                    chipView.setOnClickListener(chipModel.onClickListener)
 
                                     listener.onOngoingActivityStatusChanged(
                                         hasOngoingActivity = true
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 bb3a67e..95d9248 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
@@ -24,7 +24,7 @@
 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.statusbar.chips.domain.model.OngoingActivityChipModel
+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
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 a972985..32774e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -35,10 +35,7 @@
 @SysUISingleton
 class AvalancheController
 @Inject
-constructor(
-    dumpManager: DumpManager,
-    private val uiEventLogger: UiEventLogger
-) : Dumpable {
+constructor(dumpManager: DumpManager, private val uiEventLogger: UiEventLogger) : Dumpable {
 
     private val tag = "AvalancheController"
     private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG)
@@ -69,14 +66,11 @@
     @VisibleForTesting var debugDropSet: MutableSet<HeadsUpEntry> = HashSet()
 
     enum class ThrottleEvent(private val id: Int) : UiEventLogger.UiEventEnum {
-        @UiEvent(doc = "HUN was shown.")
-        SHOWN(1812),
-
+        @UiEvent(doc = "HUN was shown.") AVALANCHE_THROTTLING_HUN_SHOWN(1821),
         @UiEvent(doc = "HUN was dropped to show higher priority HUNs.")
-        DROPPED(1813),
-
+        AVALANCHE_THROTTLING_HUN_DROPPED(1822),
         @UiEvent(doc = "HUN was removed while waiting to show.")
-        REMOVED(1814);
+        AVALANCHE_THROTTLING_HUN_REMOVED(1823);
 
         override fun getId(): Int {
             return id
@@ -97,7 +91,7 @@
             runnable.run()
             return
         }
-        log { "\n "}
+        log { "\n " }
         val fn = "$label => AvalancheController.update ${getKey(entry)}"
         if (entry == null) {
             log { "Entry is NULL, stop update." }
@@ -129,9 +123,10 @@
                 // HeadsUpEntry.updateEntry recursively calls AvalancheController#update
                 // and goes to the isShowing case above
                 headsUpEntryShowing!!.updateEntry(
-                        /* updatePostTime= */ false,
-                        /* updateEarliestRemovalTime= */ false,
-                        /* reason= */ "avalanche duration update")
+                    /* updatePostTime= */ false,
+                    /* updateEarliestRemovalTime= */ false,
+                    /* reason= */ "avalanche duration update"
+                )
             }
         }
         logState("after $fn")
@@ -152,7 +147,7 @@
             runnable.run()
             return
         }
-        log { "\n "}
+        log { "\n " }
         val fn = "$label => AvalancheController.delete " + getKey(entry)
         if (entry == null) {
             log { "$fn => entry NULL, running runnable" }
@@ -163,7 +158,7 @@
             log { "$fn => remove from next" }
             if (entry in nextMap) nextMap.remove(entry)
             if (entry in nextList) nextList.remove(entry)
-            uiEventLogger.log(ThrottleEvent.REMOVED)
+            uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_REMOVED)
         } else if (entry in debugDropSet) {
             log { "$fn => remove from dropset" }
             debugDropSet.remove(entry)
@@ -287,7 +282,7 @@
     private fun showNow(entry: HeadsUpEntry, runnableList: MutableList<Runnable>) {
         log { "SHOW: " + getKey(entry) }
 
-        uiEventLogger.log(ThrottleEvent.SHOWN)
+        uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_SHOWN)
         headsUpEntryShowing = entry
 
         runnableList.forEach {
@@ -318,7 +313,7 @@
 
         // Log dropped HUNs
         for (e in listToDrop) {
-            uiEventLogger.log(ThrottleEvent.DROPPED)
+            uiEventLogger.log(ThrottleEvent.AVALANCHE_THROTTLING_HUN_DROPPED)
         }
 
         if (debug) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index c089092..ca94363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -31,6 +31,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.BluetoothUtils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
@@ -71,6 +72,7 @@
     private final LocalBluetoothManager mLocalBluetoothManager;
     private final UserManager mUserManager;
     private final int mCurrentUser;
+    private final Context mContext;
     @GuardedBy("mConnectedDevices")
     private final List<CachedBluetoothDevice> mConnectedDevices = new ArrayList<>();
 
@@ -99,6 +101,7 @@
             @Main Looper mainLooper,
             @Nullable LocalBluetoothManager localBluetoothManager,
             @Nullable BluetoothAdapter bluetoothAdapter) {
+        mContext = context;
         mDumpManager = dumpManager;
         mLogger = logger;
         mBluetoothRepository = bluetoothRepository;
@@ -262,9 +265,21 @@
     }
 
     private Collection<CachedBluetoothDevice> getDevices() {
-        return mLocalBluetoothManager != null
-                ? mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()
-                : Collections.emptyList();
+        Collection<CachedBluetoothDevice> devices =
+                mLocalBluetoothManager != null
+                        ? mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()
+                        : Collections.emptyList();
+        if (com.android.settingslib.flags.Flags.enableHideExclusivelyManagedBluetoothDevice()) {
+            // When the device is exclusively managed by its owner app it needs to be hidden.
+            devices =
+                    devices.stream()
+                            .filter(
+                                    device ->
+                                            !BluetoothUtils.isExclusivelyManagedBluetoothDevice(
+                                                    mContext, device.getDevice()))
+                            .toList();
+        }
+        return devices;
     }
 
     private void updateConnected() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
index abedd3220..a3dcc3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastController.java
@@ -37,15 +37,4 @@
         void onCastDevicesChanged();
     }
 
-    public static final class CastDevice {
-        public static final int STATE_DISCONNECTED = 0;
-        public static final int STATE_CONNECTING = 1;
-        public static final int STATE_CONNECTED = 2;
-
-        public String id;
-        public String name;
-        public String description;
-        public int state = STATE_DISCONNECTED;
-        public Object tag;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index 64bdf60..45cb52a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -19,15 +19,12 @@
 import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
 
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteInfo;
 import android.media.projection.MediaProjectionInfo;
 import android.media.projection.MediaProjectionManager;
 import android.os.Handler;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -37,8 +34,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.res.R;
-import com.android.systemui.util.Utils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -51,10 +46,11 @@
 /** Platform implementation of the cast controller. **/
 @SysUISingleton
 public class CastControllerImpl implements CastController {
-    private static final String TAG = "CastController";
+    public static final String TAG = "CastController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final Context mContext;
+    private final PackageManager mPackageManager;
     @GuardedBy("mCallbacks")
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final MediaRouter mMediaRouter;
@@ -68,8 +64,12 @@
     private MediaProjectionInfo mProjection;
 
     @Inject
-    public CastControllerImpl(Context context, DumpManager dumpManager) {
+    public CastControllerImpl(
+            Context context,
+            PackageManager packageManager,
+            DumpManager dumpManager) {
         mContext = context;
+        mPackageManager = packageManager;
         mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
         mMediaRouter.setRouterGroupId(MediaRouter.MIRRORING_GROUP_ID);
         mProjectionManager = (MediaProjectionManager)
@@ -156,36 +156,17 @@
         final ArrayList<CastDevice> devices = new ArrayList<>();
         synchronized(mRoutes) {
             for (RouteInfo route : mRoutes.values()) {
-                final CastDevice device = new CastDevice();
-                device.id = route.getTag().toString();
-                final CharSequence name = route.getName(mContext);
-                device.name = name != null ? name.toString() : null;
-                final CharSequence description = route.getDescription();
-                device.description = description != null ? description.toString() : null;
-
-                int statusCode = route.getStatusCode();
-                if (statusCode == RouteInfo.STATUS_CONNECTING) {
-                    device.state = CastDevice.STATE_CONNECTING;
-                } else if (route.isSelected() || statusCode == RouteInfo.STATUS_CONNECTED) {
-                    device.state = CastDevice.STATE_CONNECTED;
-                } else {
-                    device.state = CastDevice.STATE_DISCONNECTED;
-                }
-
-                device.tag = route;
-                devices.add(device);
+                devices.add(CastDevice.Companion.toCastDevice(route, mContext));
             }
         }
 
         synchronized (mProjectionLock) {
             if (mProjection != null) {
-                final CastDevice device = new CastDevice();
-                device.id = mProjection.getPackageName();
-                device.name = getAppName(mProjection.getPackageName());
-                device.description = mContext.getString(R.string.quick_settings_casting);
-                device.state = CastDevice.STATE_CONNECTED;
-                device.tag = mProjection;
-                devices.add(device);
+                devices.add(
+                        CastDevice.Companion.toCastDevice(
+                                mProjection,
+                                mContext,
+                                mPackageManager));
             }
         }
 
@@ -194,18 +175,18 @@
 
     @Override
     public void startCasting(CastDevice device) {
-        if (device == null || device.tag == null) return;
-        final RouteInfo route = (RouteInfo) device.tag;
+        if (device == null || device.getTag() == null) return;
+        final RouteInfo route = (RouteInfo) device.getTag();
         if (DEBUG) Log.d(TAG, "startCasting: " + routeToString(route));
         mMediaRouter.selectRoute(ROUTE_TYPE_REMOTE_DISPLAY, route);
     }
 
     @Override
     public void stopCasting(CastDevice device) {
-        final boolean isProjection = device.tag instanceof MediaProjectionInfo;
+        final boolean isProjection = device.getTag() instanceof MediaProjectionInfo;
         if (DEBUG) Log.d(TAG, "stopCasting isProjection=" + isProjection);
         if (isProjection) {
-            final MediaProjectionInfo projection = (MediaProjectionInfo) device.tag;
+            final MediaProjectionInfo projection = (MediaProjectionInfo) device.getTag();
             if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) {
                 mProjectionManager.stopActiveProjection();
             } else {
@@ -219,7 +200,7 @@
     @Override
     public boolean hasConnectedCastDevice() {
         return getCastDevices().stream().anyMatch(
-                castDevice -> castDevice.state == CastDevice.STATE_CONNECTED);
+                castDevice -> castDevice.getState() == CastDevice.CastState.Connected);
     }
 
     private void setProjection(MediaProjectionInfo projection, boolean started) {
@@ -241,27 +222,6 @@
         }
     }
 
-    private String getAppName(String packageName) {
-        final PackageManager pm = mContext.getPackageManager();
-        if (Utils.isHeadlessRemoteDisplayProvider(pm, packageName)) {
-            return "";
-        }
-
-        try {
-            final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
-            if (appInfo != null) {
-                final CharSequence label = appInfo.loadLabel(pm);
-                if (!TextUtils.isEmpty(label)) {
-                    return label.toString();
-                }
-            }
-            Log.w(TAG, "No label found for package: " + packageName);
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Error getting appName for package: " + packageName, e);
-        }
-        return packageName;
-    }
-
     private void updateRemoteDisplays() {
         synchronized(mRoutes) {
             mRoutes.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt
new file mode 100644
index 0000000..5fc160b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastDevice.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.policy
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.media.MediaRouter
+import android.media.projection.MediaProjectionInfo
+import android.text.TextUtils
+import android.util.Log
+import com.android.systemui.res.R
+import com.android.systemui.util.Utils
+
+/** Represents a specific cast session. */
+data class CastDevice(
+    val id: String,
+    /** A human-readable name of what is receiving the cast (e.g. "Home Speaker", "Abc App"). */
+    val name: String?,
+    /** An optional description with more information about the cast. */
+    val description: String? = null,
+    val state: CastState,
+    val origin: CastOrigin,
+    /** Optional tag to use as a comparison value between cast sessions. */
+    val tag: Any? = null,
+) {
+    val isCasting = state == CastState.Connecting || state == CastState.Connected
+
+    companion object {
+        /** Creates a [CastDevice] based on the provided information from MediaRouter. */
+        fun MediaRouter.RouteInfo.toCastDevice(context: Context): CastDevice {
+            val state =
+                when {
+                    statusCode == MediaRouter.RouteInfo.STATUS_CONNECTING -> CastState.Connecting
+                    this.isSelected || statusCode == MediaRouter.RouteInfo.STATUS_CONNECTED ->
+                        CastState.Connected
+                    else -> CastState.Disconnected
+                }
+            return CastDevice(
+                id = this.tag.toString(),
+                name = this.getName(context)?.toString(),
+                description = this.description?.toString(),
+                state = state,
+                tag = this,
+                origin = CastOrigin.MediaRouter,
+            )
+        }
+
+        /** Creates a [CastDevice] based on the provided information from MediaProjection. */
+        fun MediaProjectionInfo.toCastDevice(
+            context: Context,
+            packageManager: PackageManager
+        ): CastDevice {
+            return CastDevice(
+                id = this.packageName,
+                name = getAppName(this.packageName, packageManager),
+                description = context.getString(R.string.quick_settings_casting),
+                state = CastState.Connected,
+                tag = this,
+                origin = CastOrigin.MediaProjection,
+            )
+        }
+
+        private fun getAppName(packageName: String, packageManager: PackageManager): String {
+            if (Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)) {
+                return ""
+            }
+            try {
+                val appInfo = packageManager.getApplicationInfo(packageName, 0)
+                val label = appInfo.loadLabel(packageManager)
+                if (!TextUtils.isEmpty(label)) {
+                    return label.toString()
+                }
+                Log.w(CastControllerImpl.TAG, "No label found for package: $packageName")
+            } catch (e: PackageManager.NameNotFoundException) {
+                Log.w(CastControllerImpl.TAG, "Error getting appName for package: $packageName", e)
+            }
+            return packageName
+        }
+    }
+
+    enum class CastState {
+        Disconnected,
+        Connecting,
+        Connected,
+    }
+
+    enum class CastOrigin {
+        /** SysUI found out about this cast device from MediaRouter APIs. */
+        MediaRouter,
+        /** SysUI found out about this cast device from MediaProjection APIs. */
+        MediaProjection,
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index 08f973b..2ce2bc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.qs.tiles.MicrophoneToggleTile
 import com.android.systemui.qs.tiles.UiModeNightTile
 import com.android.systemui.qs.tiles.WorkModeTile
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
 import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
 import com.android.systemui.qs.tiles.impl.alarm.domain.AlarmTileMapper
 import com.android.systemui.qs.tiles.impl.alarm.domain.interactor.AlarmTileDataInteractor
@@ -80,6 +81,41 @@
     @StringKey(WorkModeTile.TILE_SPEC)
     fun bindWorkModeTile(workModeTile: WorkModeTile): QSTileImpl<*>
 
+    @Binds
+    @IntoMap
+    @StringKey(FLASHLIGHT_TILE_SPEC)
+    fun provideAirplaneModeAvailabilityInteractor(
+            impl: FlashlightTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(LOCATION_TILE_SPEC)
+    fun provideLocationAvailabilityInteractor(
+            impl: LocationTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(ALARM_TILE_SPEC)
+    fun provideAlarmAvailabilityInteractor(
+            impl: AlarmTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(UIMODENIGHT_TILE_SPEC)
+    fun provideUiModeNightAvailabilityInteractor(
+            impl: UiModeNightTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
+    @Binds
+    @IntoMap
+    @StringKey(WORK_MODE_TILE_SPEC)
+    fun provideWorkModeAvailabilityInteractor(
+            impl: WorkModeTileDataInteractor
+    ): QSTileAvailabilityInteractor
+
     companion object {
         const val FLASHLIGHT_TILE_SPEC = "flashlight"
         const val LOCATION_TILE_SPEC = "location"
@@ -287,6 +323,15 @@
                 mapper.create(SensorPrivacyTileResources.CameraPrivacyTileResources),
             )
 
+        @Provides
+        @IntoMap
+        @StringKey(CAMERA_TOGGLE_TILE_SPEC)
+        fun provideCameraToggleAvailabilityInteractor(
+                factory: SensorPrivacyToggleTileDataInteractor.Factory
+        ): QSTileAvailabilityInteractor {
+            return factory.create(CAMERA)
+        }
+
         /** Inject microphone toggle config */
         @Provides
         @IntoMap
@@ -320,6 +365,16 @@
                 mapper.create(SensorPrivacyTileResources.MicrophonePrivacyTileResources),
             )
 
+        @Provides
+        @IntoMap
+        @StringKey(MIC_TOGGLE_TILE_SPEC)
+        fun provideMicToggleModeAvailabilityInteractor(
+                factory: SensorPrivacyToggleTileDataInteractor.Factory
+        ): QSTileAvailabilityInteractor {
+            return factory.create(MICROPHONE)
+        }
+
+
         /** Inject microphone toggle config */
         @Provides
         @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/GestureViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/GestureViewModelFactory.kt
new file mode 100644
index 0000000..504bd5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/GestureViewModelFactory.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+sealed class GestureTutorialViewModel : ViewModel()
+
+class BackGestureTutorialViewModel : GestureTutorialViewModel()
+
+class HomeGestureTutorialViewModel : GestureTutorialViewModel()
+
+class GestureViewModelFactory : ViewModelProvider.Factory {
+
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : ViewModel> create(modelClass: Class<T>): T {
+        return when (modelClass) {
+            BackGestureTutorialViewModel::class.java -> BackGestureTutorialViewModel()
+            HomeGestureTutorialViewModel::class.java -> HomeGestureTutorialViewModel()
+            else -> error("Unknown ViewModel class: ${modelClass.name}")
+        }
+            as T
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt
new file mode 100644
index 0000000..11740a8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.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.touchpad.tutorial.ui
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import javax.inject.Inject
+
+class TouchpadTutorialViewModel : ViewModel() {
+
+    private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION)
+    val screen: StateFlow<Screen> = _screen
+
+    fun goTo(screen: Screen) {
+        _screen.value = screen
+    }
+
+    class Factory @Inject constructor() : ViewModelProvider.Factory {
+
+        @Suppress("UNCHECKED_CAST")
+        override fun <T : ViewModel> create(modelClass: Class<T>): T {
+            return TouchpadTutorialViewModel() as T
+        }
+    }
+}
+
+enum class Screen {
+    TUTORIAL_SELECTION,
+    BACK_GESTURE,
+    HOME_GESTURE,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
new file mode 100644
index 0000000..b7629c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.touchpad.tutorial.ui.view
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.Lifecycle.State.STARTED
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.touchpad.tutorial.ui.BackGestureTutorialViewModel
+import com.android.systemui.touchpad.tutorial.ui.GestureViewModelFactory
+import com.android.systemui.touchpad.tutorial.ui.HomeGestureTutorialViewModel
+import com.android.systemui.touchpad.tutorial.ui.Screen.BACK_GESTURE
+import com.android.systemui.touchpad.tutorial.ui.Screen.HOME_GESTURE
+import com.android.systemui.touchpad.tutorial.ui.Screen.TUTORIAL_SELECTION
+import com.android.systemui.touchpad.tutorial.ui.TouchpadTutorialViewModel
+import javax.inject.Inject
+
+class TouchpadTutorialActivity
+@Inject
+constructor(
+    private val viewModelFactory: TouchpadTutorialViewModel.Factory,
+) : ComponentActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        enableEdgeToEdge()
+        setContent {
+            PlatformTheme { TouchpadTutorialScreen(viewModelFactory, closeTutorial = { finish() }) }
+        }
+    }
+}
+
+@Composable
+fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory, closeTutorial: () -> Unit) {
+    val vm = viewModel<TouchpadTutorialViewModel>(factory = viewModelFactory)
+    val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED)
+    when (activeScreen) {
+        TUTORIAL_SELECTION ->
+            TutorialSelectionScreen(
+                onBackTutorialClicked = { vm.goTo(BACK_GESTURE) },
+                onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) },
+                onActionKeyTutorialClicked = {},
+                onDoneButtonClicked = closeTutorial
+            )
+        BACK_GESTURE -> BackGestureTutorialScreen()
+        HOME_GESTURE -> HomeGestureTutorialScreen()
+    }
+}
+
+@Composable
+fun BackGestureTutorialScreen() {
+    val vm = viewModel<BackGestureTutorialViewModel>(factory = GestureViewModelFactory())
+}
+
+@Composable
+fun HomeGestureTutorialScreen() {
+    val vm = viewModel<HomeGestureTutorialViewModel>(factory = GestureViewModelFactory())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt
new file mode 100644
index 0000000..532eb1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.touchpad.tutorial.ui.view
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+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.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.android.systemui.res.R
+
+@Composable
+fun TutorialSelectionScreen(
+    onBackTutorialClicked: () -> Unit,
+    onHomeTutorialClicked: () -> Unit,
+    onActionKeyTutorialClicked: () -> Unit,
+    onDoneButtonClicked: () -> Unit,
+) {
+    Column(
+        verticalArrangement = Arrangement.Center,
+        modifier =
+            Modifier.background(
+                    color = MaterialTheme.colorScheme.surfaceContainer,
+                )
+                .fillMaxSize()
+    ) {
+        TutorialSelectionButtons(
+            onBackTutorialClicked = onBackTutorialClicked,
+            onHomeTutorialClicked = onHomeTutorialClicked,
+            onActionKeyTutorialClicked = onActionKeyTutorialClicked,
+            modifier = Modifier.padding(60.dp)
+        )
+        DoneButton(
+            onDoneButtonClicked = onDoneButtonClicked,
+            modifier = Modifier.padding(horizontal = 60.dp)
+        )
+    }
+}
+
+@Composable
+private fun TutorialSelectionButtons(
+    onBackTutorialClicked: () -> Unit,
+    onHomeTutorialClicked: () -> Unit,
+    onActionKeyTutorialClicked: () -> Unit,
+    modifier: Modifier = Modifier
+) {
+    Row(
+        horizontalArrangement = Arrangement.spacedBy(20.dp),
+        verticalAlignment = Alignment.CenterVertically,
+        modifier = modifier
+    ) {
+        TutorialButton(
+            text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
+            onClick = onBackTutorialClicked,
+            color = MaterialTheme.colorScheme.primary,
+            modifier = Modifier.weight(1f)
+        )
+        TutorialButton(
+            text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
+            onClick = onHomeTutorialClicked,
+            color = MaterialTheme.colorScheme.secondary,
+            modifier = Modifier.weight(1f)
+        )
+        TutorialButton(
+            text = stringResource(R.string.touchpad_tutorial_action_key_button),
+            onClick = onActionKeyTutorialClicked,
+            color = MaterialTheme.colorScheme.tertiary,
+            modifier = Modifier.weight(1f)
+        )
+    }
+}
+
+@Composable
+private fun TutorialButton(
+    text: String,
+    onClick: () -> Unit,
+    color: Color,
+    modifier: Modifier = Modifier
+) {
+    Button(
+        onClick = onClick,
+        shape = RoundedCornerShape(16.dp),
+        colors = ButtonDefaults.buttonColors(containerColor = color),
+        modifier = modifier.aspectRatio(0.66f)
+    ) {
+        Text(text = text, style = MaterialTheme.typography.headlineLarge)
+    }
+}
+
+@Composable
+private fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) {
+    Row(
+        horizontalArrangement = Arrangement.End,
+        verticalAlignment = Alignment.CenterVertically,
+        modifier = modifier.fillMaxWidth()
+    ) {
+        Button(onClick = onDoneButtonClicked) {
+            Text(stringResource(R.string.touchpad_tutorial_done_button))
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index 139ac7e..291903d 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.shade.NotificationPanelUnfoldAnimationController
 import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
+import com.android.systemui.unfold.dagger.NaturalRotation
 import com.android.systemui.unfold.dagger.UnfoldBg
 import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -36,6 +37,7 @@
 import dagger.multibindings.IntoSet
 import java.util.Optional
 import javax.inject.Named
+import javax.inject.Qualifier
 import javax.inject.Scope
 
 @Scope @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class SysUIUnfoldScope
@@ -54,8 +56,17 @@
 @Module(subcomponents = [SysUIUnfoldComponent::class])
 class SysUIUnfoldModule {
 
+    /**
+     * Qualifier for dependencies bound in [com.android.systemui.unfold.SysUIUnfoldModule]
+     */
+    @Qualifier
+    @MustBeDocumented
+    @Retention(AnnotationRetention.RUNTIME)
+    annotation class BoundFromSysUiUnfoldModule
+
     @Provides
     @SysUISingleton
+    @BoundFromSysUiUnfoldModule
     fun provideSysUIUnfoldComponent(
         provider: Optional<UnfoldTransitionProgressProvider>,
         rotationProvider: Optional<NaturalRotationUnfoldProgressProvider>,
@@ -98,6 +109,13 @@
     abstract fun bindsFoldLightRevealOverlayAnimation(
         anim: FoldLightRevealOverlayAnimation
     ): FullscreenLightRevealAnimation
+
+    @Binds
+    @NaturalRotation
+    @SysUIUnfoldScope
+    abstract fun bindNaturalRotationUnfoldProgressProvider(
+        provider: NaturalRotationUnfoldProgressProvider
+    ): UnfoldTransitionProgressProvider
 }
 
 @SysUIUnfoldScope
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt
index 41cd95b..8d202ac 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/MapUtils.kt
@@ -30,3 +30,6 @@
     }
     return destination
 }
+
+/** Returns a map with all entries containing `null` values removed. */
+fun <K, V> Map<K, V?>.filterValuesNotNull(): Map<K, V> = mapValuesNotNull { it.value }
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
index 405b57a..d9a2e95 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Utils.kt
@@ -19,19 +19,24 @@
 import android.content.Context
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
 
 class Utils {
     companion object {
         fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
+
         fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c)
 
         fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
+
         fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) =
             Quad(a, bcd.first, bcd.second, bcd.third)
         fun <A, B, C, D> toQuad(abc: Triple<A, B, C>, d: D) =
             Quad(abc.first, abc.second, abc.third, d)
 
         fun <A, B, C, D, E> toQuint(a: A, b: B, c: C, d: D, e: E) = Quint(a, b, c, d, e)
+
         fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
             Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth)
 
@@ -50,6 +55,14 @@
             )
 
         /**
+         * Samples the provided flow, performs a filter on the sampled value, then returns the
+         * original value.
+         */
+        fun <A, B> Flow<A>.sampleFilter(b: Flow<B>, predicate: (B) -> Boolean): Flow<A> {
+            return this.sample(b, ::Pair).filter { (_, b) -> predicate(b) }.map { (a, _) -> a }
+        }
+
+        /**
          * Samples the provided flows, emitting a tuple of the original flow's value as well as each
          * of the combined flows' values.
          *
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
index 4438763..42389f0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/GlobalSettingsImpl.java
@@ -23,23 +23,16 @@
 import android.net.Uri;
 import android.provider.Settings;
 
-import com.android.systemui.dagger.qualifiers.Background;
-
-import kotlinx.coroutines.CoroutineDispatcher;
-
 import javax.inject.Inject;
 
 // use UserHandle.USER_SYSTEM everywhere
 @SuppressLint("StaticSettingsProvider")
 class GlobalSettingsImpl implements GlobalSettings {
     private final ContentResolver mContentResolver;
-    private final CoroutineDispatcher mBgDispatcher;
 
     @Inject
-    GlobalSettingsImpl(ContentResolver contentResolver,
-            @Background CoroutineDispatcher bgDispatcher) {
+    GlobalSettingsImpl(ContentResolver contentResolver) {
         mContentResolver = contentResolver;
-        mBgDispatcher = bgDispatcher;
     }
 
     @Override
@@ -53,11 +46,6 @@
     }
 
     @Override
-    public CoroutineDispatcher getBackgroundDispatcher() {
-        return mBgDispatcher;
-    }
-
-    @Override
     public String getString(String name) {
         return Settings.Global.getString(mContentResolver, name);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
index 38ad5d0..6532ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SecureSettingsImpl.java
@@ -22,24 +22,18 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.settings.UserTracker;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-
 import javax.inject.Inject;
 
 class SecureSettingsImpl implements SecureSettings {
     private final ContentResolver mContentResolver;
     private final UserTracker mUserTracker;
-    private final CoroutineDispatcher mBgDispatcher;
 
     @Inject
-    SecureSettingsImpl(ContentResolver contentResolver, UserTracker userTracker,
-            @Background CoroutineDispatcher bgDispatcher) {
+    SecureSettingsImpl(ContentResolver contentResolver, UserTracker userTracker) {
         mContentResolver = contentResolver;
         mUserTracker = userTracker;
-        mBgDispatcher = bgDispatcher;
     }
 
     @Override
@@ -58,11 +52,6 @@
     }
 
     @Override
-    public CoroutineDispatcher getBackgroundDispatcher() {
-        return mBgDispatcher;
-    }
-
-    @Override
     public String getStringForUser(String name, int userHandle) {
         return Settings.Secure.getStringForUser(mContentResolver, name,
                 getRealUserHandle(userHandle));
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 55171ad..d92127c 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -19,10 +19,7 @@
 import android.database.ContentObserver
 import android.net.Uri
 import android.provider.Settings.SettingNotFoundException
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+import com.android.app.tracing.TraceUtils.trace
 
 /**
  * Used to interact with mainly with Settings.Global, but can also be used for Settings.System and
@@ -43,12 +40,6 @@
     fun getContentResolver(): ContentResolver
 
     /**
-     * Returns the background [CoroutineDispatcher] that the async APIs will use for a specific
-     * implementation.
-     */
-    fun getBackgroundDispatcher(): CoroutineDispatcher
-
-    /**
      * Construct the content URI for a particular name/value pair, useful for monitoring changes
      * with a ContentObserver.
      *
@@ -66,28 +57,6 @@
         registerContentObserverSync(getUriFor(name), settingsObserver)
     }
 
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * suspend API corresponding to [registerContentObserver] to ensure that [ContentObserver]
-     * registration happens on a worker thread. Caller may wrap the API in an async block if they
-     * wish to synchronize execution.
-     */
-    suspend fun registerContentObserver(name: String, settingsObserver: ContentObserver) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverSync(getUriFor(name), settingsObserver)
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserver] for Java usage.
-     */
-    fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverSync(getUriFor(name), settingsObserver)
-        }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
     fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) =
         registerContentObserverSync(uri, false, settingsObserver)
@@ -95,28 +64,6 @@
     /**
      * Convenience wrapper around [ContentResolver.registerContentObserver].'
      *
-     * suspend API corresponding to [registerContentObserver] to ensure that [ContentObserver]
-     * registration happens on a worker thread. Caller may wrap the API in an async block if they
-     * wish to synchronize execution.
-     */
-    suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverSync(uri, settingsObserver)
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserver] for Java usage.
-     */
-    fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverSync(uri, settingsObserver)
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
      * Implicitly calls [getUriFor] on the passed in name.
      */
     fun registerContentObserverSync(
@@ -125,100 +72,24 @@
         settingsObserver: ContentObserver
     ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
 
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * suspend API corresponding to [registerContentObserver] to ensure that [ContentObserver]
-     * registration happens on a worker thread. Caller may wrap the API in an async block if they
-     * wish to synchronize execution.
-     */
-    suspend fun registerContentObserver(
-        name: String,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
-    ) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserver] for Java usage.
-     */
-    fun registerContentObserverAsync(
-        name: String,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
-    ) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
-        }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
     fun registerContentObserverSync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver
     ) {
-        getContentResolver().registerContentObserver(uri, notifyForDescendants, settingsObserver)
-    }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * suspend API corresponding to [registerContentObserver] to ensure that [ContentObserver]
-     * registration happens on a worker thread. Caller may wrap the API in an async block if they
-     * wish to synchronize execution.
-     */
-    suspend fun registerContentObserver(
-        uri: Uri,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
-    ) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverSync(uri, notifyForDescendants, settingsObserver)
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserver] for Java usage.
-     */
-    fun registerContentObserverAsync(
-        uri: Uri,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
-    ) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
+        trace({ "SP#registerObserver#[$uri]" }) {
             getContentResolver()
                 .registerContentObserver(uri, notifyForDescendants, settingsObserver)
         }
+    }
 
     /** See [ContentResolver.unregisterContentObserver]. */
-    fun unregisterContentObserverSync(settingsObserver: ContentObserver) =
-        getContentResolver().unregisterContentObserver(settingsObserver)
-
-    /**
-     * Convenience wrapper around [ContentResolver.unregisterContentObserver].'
-     *
-     * API corresponding to [unregisterContentObserver] for Java usage to ensure that
-     * [ContentObserver] un-registration happens on a worker thread. Caller may wrap the API in an
-     * async block if they wish to synchronize execution.
-     */
-    suspend fun unregisterContentObserver(settingsObserver: ContentObserver) =
-        withContext(getBackgroundDispatcher()) { unregisterContentObserverSync(settingsObserver) }
-
-    /**
-     * Convenience wrapper around [ContentResolver.unregisterContentObserver].'
-     *
-     * API corresponding to [unregisterContentObserver] for Java usage to ensure that
-     * [ContentObserver] registration happens on a worker thread.
-     */
-    fun unregisterContentObserverAsync(settingsObserver: ContentObserver) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            unregisterContentObserver(settingsObserver)
+    fun unregisterContentObserverSync(settingsObserver: ContentObserver) {
+        trace({ "SP#unregisterObserver" }) {
+            getContentResolver().unregisterContentObserver(settingsObserver)
         }
+    }
 
     /**
      * Look up a name in the database.
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
index 68cc753..658b299 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SystemSettingsImpl.java
@@ -22,24 +22,18 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.settings.UserTracker;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-
 import javax.inject.Inject;
 
 class SystemSettingsImpl implements SystemSettings {
     private final ContentResolver mContentResolver;
     private final UserTracker mUserTracker;
-    private final CoroutineDispatcher mBgCoroutineDispatcher;
 
     @Inject
-    SystemSettingsImpl(ContentResolver contentResolver, UserTracker userTracker,
-            @Background CoroutineDispatcher bgDispatcher) {
+    SystemSettingsImpl(ContentResolver contentResolver, UserTracker userTracker) {
         mContentResolver = contentResolver;
         mUserTracker = userTracker;
-        mBgCoroutineDispatcher = bgDispatcher;
     }
 
     @Override
@@ -58,11 +52,6 @@
     }
 
     @Override
-    public CoroutineDispatcher getBackgroundDispatcher() {
-        return mBgCoroutineDispatcher;
-    }
-
-    @Override
     public String getStringForUser(String name, int userHandle) {
         return Settings.System.getStringForUser(mContentResolver, name,
                 getRealUserHandle(userHandle));
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 9133fbb..ed65f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -16,7 +16,6 @@
 package com.android.systemui.util.settings
 
 import android.annotation.UserIdInt
-import android.content.ContentResolver
 import android.database.ContentObserver
 import android.net.Uri
 import android.os.UserHandle
@@ -27,9 +26,6 @@
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
 import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrUseDefault
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
 
 /**
  * Used to interact with per-user Settings.Secure and Settings.System settings (but not
@@ -70,16 +66,6 @@
         registerContentObserverForUserSync(uri, settingsObserver, userId)
     }
 
-    override suspend fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverForUserSync(uri, settingsObserver, userId)
-        }
-
-    override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverForUserSync(uri, settingsObserver, userId)
-        }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
     override fun registerContentObserverSync(
         uri: Uri,
@@ -89,29 +75,6 @@
         registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
     }
 
-    override suspend fun registerContentObserver(
-        uri: Uri,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
-    ) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserverForUser] for Java usage.
-     */
-    override fun registerContentObserverAsync(
-        uri: Uri,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver
-    ) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
-        }
-
     /**
      * Convenience wrapper around [ContentResolver.registerContentObserver]
      *
@@ -125,36 +88,6 @@
         registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
     }
 
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * suspend API corresponding to [registerContentObserverForUser] to ensure that
-     * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
-     * async block if they wish to synchronize execution.
-     */
-    suspend fun registerContentObserverForUser(
-        name: String,
-        settingsObserver: ContentObserver,
-        userHandle: Int
-    ) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverForUserSync(name, settingsObserver, userHandle)
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserverForUser] for Java usage.
-     */
-    fun registerContentObserverForUserAsync(
-        name: String,
-        settingsObserver: ContentObserver,
-        userHandle: Int
-    ) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
-        }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver] */
     fun registerContentObserverForUserSync(
         uri: Uri,
@@ -165,36 +98,6 @@
     }
 
     /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * suspend API corresponding to [registerContentObserverForUser] to ensure that
-     * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
-     * async block if they wish to synchronize execution.
-     */
-    suspend fun registerContentObserverForUser(
-        uri: Uri,
-        settingsObserver: ContentObserver,
-        userHandle: Int
-    ) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverForUserSync(uri, settingsObserver, userHandle)
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserverForUser] for Java usage.
-     */
-    fun registerContentObserverForUserAsync(
-        uri: Uri,
-        settingsObserver: ContentObserver,
-        userHandle: Int
-    ) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverForUserSync(uri, settingsObserver, userHandle)
-        }
-
-    /**
      * Convenience wrapper around [ContentResolver.registerContentObserver]
      *
      * Implicitly calls [getUriFor] on the passed in name.
@@ -213,49 +116,6 @@
         )
     }
 
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * suspend API corresponding to [registerContentObserverForUser] to ensure that
-     * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
-     * async block if they wish to synchronize execution.
-     */
-    suspend fun registerContentObserverForUser(
-        name: String,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver,
-        userHandle: Int
-    ) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverForUserSync(
-                name,
-                notifyForDescendants,
-                settingsObserver,
-                userHandle
-            )
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserverForUser] for Java usage.
-     */
-    fun registerContentObserverForUserAsync(
-        name: String,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver,
-        userHandle: Int
-    ) {
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverForUserSync(
-                getUriFor(name),
-                notifyForDescendants,
-                settingsObserver,
-                userHandle
-            )
-        }
-    }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver] */
     fun registerContentObserverForUserSync(
         uri: Uri,
@@ -276,48 +136,6 @@
     }
 
     /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * suspend API corresponding to [registerContentObserverForUser] to ensure that
-     * [ContentObserver] registration happens on a worker thread. Caller may wrap the API in an
-     * async block if they wish to synchronize execution.
-     */
-    suspend fun registerContentObserverForUser(
-        uri: Uri,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver,
-        userHandle: Int
-    ) =
-        withContext(getBackgroundDispatcher()) {
-            registerContentObserverForUserSync(
-                uri,
-                notifyForDescendants,
-                settingsObserver,
-                getRealUserHandle(userHandle)
-            )
-        }
-
-    /**
-     * Convenience wrapper around [ContentResolver.registerContentObserver].'
-     *
-     * API corresponding to [registerContentObserverForUser] for Java usage.
-     */
-    fun registerContentObserverForUserAsync(
-        uri: Uri,
-        notifyForDescendants: Boolean,
-        settingsObserver: ContentObserver,
-        userHandle: Int
-    ) =
-        CoroutineScope(getBackgroundDispatcher()).launch {
-            registerContentObserverForUserSync(
-                uri,
-                notifyForDescendants,
-                settingsObserver,
-                userHandle
-            )
-        }
-
-    /**
      * Look up a name in the database.
      *
      * @param name to look up in the table
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index ce5545c..795d427 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -135,7 +135,9 @@
 import com.android.systemui.util.AlphaTintDrawableWrapper;
 import com.android.systemui.util.RoundedCornerProgressDrawable;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
 import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
 import com.android.systemui.volume.ui.navigation.VolumeNavigator;
 
 import dagger.Lazy;
@@ -311,6 +313,8 @@
     private int mDialogTimeoutMillis;
     private final VibratorHelper mVibratorHelper;
     private final com.android.systemui.util.time.SystemClock mSystemClock;
+    private final VolumePanelFlag mVolumePanelFlag;
+    private final VolumeDialogInteractor mInteractor;
 
     public VolumeDialogImpl(
             Context context,
@@ -326,10 +330,12 @@
             CsdWarningDialog.Factory csdWarningDialogFactory,
             DevicePostureController devicePostureController,
             Looper looper,
+            VolumePanelFlag volumePanelFlag,
             DumpManager dumpManager,
             Lazy<SecureSettings> secureSettings,
             VibratorHelper vibratorHelper,
-            com.android.systemui.util.time.SystemClock systemClock) {
+            com.android.systemui.util.time.SystemClock systemClock,
+            VolumeDialogInteractor interactor) {
         mContext =
                 new ContextThemeWrapper(context, R.style.volume_dialog_theme);
         mHandler = new H(looper);
@@ -362,6 +368,8 @@
         mVolumeNavigator = volumeNavigator;
         mSecureSettings = secureSettings;
         mDialogTimeoutMillis = DIALOG_TIMEOUT_MILLIS;
+        mVolumePanelFlag = volumePanelFlag;
+        mInteractor = interactor;
 
         dumpManager.registerDumpable("VolumeDialogImpl", this);
 
@@ -1358,6 +1366,9 @@
     }
 
     private void updateODICaptionsH(boolean isServiceComponentEnabled, boolean fromTooltip) {
+        // don't show captions view when the new volume panel is enabled.
+        isServiceComponentEnabled =
+                isServiceComponentEnabled && !mVolumePanelFlag.canUseNewVolumePanel();
         if (mODICaptionsView != null) {
             mODICaptionsView.setVisibility(isServiceComponentEnabled ? VISIBLE : GONE);
         }
@@ -1506,6 +1517,7 @@
         mShowing = true;
         mIsAnimatingDismiss = false;
         mDialog.show();
+        mInteractor.onDialogShown();
         Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked);
         mController.notifyVisible(true);
         mController.getCaptionsComponentState(false);
@@ -1592,6 +1604,7 @@
         }
         mIsAnimatingDismiss = true;
         mDialogView.animate().cancel();
+        mInteractor.onDialogDismissed();
         if (mShowing) {
             mShowing = false;
             // Only logs when the volume dialog visibility is changed.
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index dc1e8cf..5420988 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -40,9 +40,11 @@
 import com.android.systemui.volume.VolumeDialogImpl;
 import com.android.systemui.volume.VolumePanelDialogReceiver;
 import com.android.systemui.volume.VolumeUI;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
 import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
 import com.android.systemui.volume.panel.dagger.VolumePanelComponent;
 import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
 import com.android.systemui.volume.ui.navigation.VolumeNavigator;
 
 import dagger.Binds;
@@ -109,10 +111,12 @@
             VolumeNavigator volumeNavigator,
             CsdWarningDialog.Factory csdFactory,
             DevicePostureController devicePostureController,
+            VolumePanelFlag volumePanelFlag,
             DumpManager dumpManager,
             Lazy<SecureSettings> secureSettings,
             VibratorHelper vibratorHelper,
-            SystemClock systemClock) {
+            SystemClock systemClock,
+            VolumeDialogInteractor interactor) {
         VolumeDialogImpl impl = new VolumeDialogImpl(
                 context,
                 volumeDialogController,
@@ -127,10 +131,12 @@
                 csdFactory,
                 devicePostureController,
                 Looper.getMainLooper(),
+                volumePanelFlag,
                 dumpManager,
                 secureSettings,
                 vibratorHelper,
-                systemClock);
+                systemClock,
+                interactor);
         impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
         impl.setAutomute(true);
         impl.setSilentMode(false);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.kt
new file mode 100644
index 0000000..75e1c5ac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/data/repository/VolumeDialogRepository.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.volume.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** A repository that encapsulates the states for Volume Dialog. */
+@SysUISingleton
+class VolumeDialogRepository @Inject constructor() {
+    private val _isDialogVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    /** Whether the Volume Dialog is visible. */
+    val isDialogVisible = _isDialogVisible.asStateFlow()
+
+    /** Sets whether the Volume Dialog is visible. */
+    fun setDialogVisibility(isVisible: Boolean) {
+        _isDialogVisible.value = isVisible
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.kt
new file mode 100644
index 0000000..813e707
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractor.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.volume.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.volume.data.repository.VolumeDialogRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+/** An interactor that propagates the UI states of the Volume Dialog. */
+@SysUISingleton
+class VolumeDialogInteractor
+@Inject
+constructor(
+    private val repository: VolumeDialogRepository,
+) {
+    /** Whether the Volume Dialog is visible. */
+    val isDialogVisible: StateFlow<Boolean> = repository.isDialogVisible
+
+    /** Notifies that the Volume Dialog is shown. */
+    fun onDialogShown() {
+        repository.setDialogVisibility(true)
+    }
+
+    /** Notifies that the Volume Dialog has been dismissed. */
+    fun onDialogDismissed() {
+        repository.setDialogVisibility(false)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
index 9ca50d6..7f1faee 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
@@ -74,10 +74,10 @@
             ) { attributes, _,
                 ->
                 attributes ?: return@combine SpatialAudioAvailabilityModel.Unavailable
-                if (spatializerInteractor.isHeadTrackingAvailable(attributes)) {
-                    return@combine SpatialAudioAvailabilityModel.HeadTracking
-                }
                 if (spatializerInteractor.isSpatialAudioAvailable(attributes)) {
+                    if (spatializerInteractor.isHeadTrackingAvailable(attributes)) {
+                        return@combine SpatialAudioAvailabilityModel.HeadTracking
+                    }
                     return@combine SpatialAudioAvailabilityModel.SpatialAudio
                 }
                 SpatialAudioAvailabilityModel.Unavailable
@@ -104,10 +104,10 @@
                     return@combine SpatialAudioEnabledModel.Disabled
                 }
                 attributes ?: return@combine SpatialAudioEnabledModel.Disabled
-                if (spatializerInteractor.isHeadTrackingEnabled(attributes)) {
-                    return@combine SpatialAudioEnabledModel.HeadTrackingEnabled
-                }
                 if (spatializerInteractor.isSpatialAudioEnabled(attributes)) {
+                    if (spatializerInteractor.isHeadTrackingEnabled(attributes)) {
+                        return@combine SpatialAudioEnabledModel.HeadTrackingEnabled
+                    }
                     return@combine SpatialAudioEnabledModel.SpatialAudioEnabled
                 }
                 SpatialAudioEnabledModel.Disabled
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index 6c6a1cc..324579d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -57,7 +57,7 @@
                     .toViewModel(
                         isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled,
                         isHeadTrackingAvailable =
-                            isAvailable is SpatialAudioAvailabilityModel.SpatialAudio,
+                            isAvailable is SpatialAudioAvailabilityModel.HeadTracking,
                     )
                     .copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
             }
@@ -69,7 +69,7 @@
                 // head tracking availability means there are three possible states for the spatial
                 // audio: disabled, enabled regular, enabled with head tracking.
                 // Show popup in this case instead of a togglealbe button.
-                it is SpatialAudioAvailabilityModel.SpatialAudio
+                it is SpatialAudioAvailabilityModel.HeadTracking
             }
             .stateIn(scope, SharingStarted.Eagerly, false)
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 850162e..c18573e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -32,9 +32,13 @@
 import dagger.assisted.AssistedInject
 import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
@@ -49,6 +53,7 @@
     private val uiEventLogger: UiEventLogger,
 ) : SliderViewModel {
 
+    private val volumeChanges = MutableStateFlow<Int?>(null)
     private val streamsAffectedByRing =
         setOf(
             AudioManager.STREAM_RING,
@@ -104,12 +109,17 @@
             }
             .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
 
+    init {
+        volumeChanges
+            .filterNotNull()
+            .onEach { audioVolumeInteractor.setVolume(audioStream, it) }
+            .launchIn(coroutineScope)
+    }
+
     override fun onValueChanged(state: SliderState, newValue: Float) {
         val audioViewModel = state as? State
         audioViewModel ?: return
-        coroutineScope.launch {
-            audioVolumeInteractor.setVolume(audioStream, newValue.roundToInt())
-        }
+        volumeChanges.tryEmit(newValue.roundToInt())
     }
 
     override fun onValueChangeFinished() {
diff --git a/packages/SystemUI/tests/goldens/doubleClick_swapSide.json b/packages/SystemUI/tests/goldens/doubleClick_swapSide.json
index 044ddbc..c4998a5 100644
--- a/packages/SystemUI/tests/goldens/doubleClick_swapSide.json
+++ b/packages/SystemUI/tests/goldens/doubleClick_swapSide.json
@@ -38,7 +38,7 @@
           "y": 96
         },
         {
-          "x": 45.008995,
+          "x": 45.009,
           "y": 96
         },
         {
@@ -133,7 +133,7 @@
           "y": 96
         },
         {
-          "x": 156.13857,
+          "x": 156.13858,
           "y": 96
         },
         {
@@ -141,7 +141,7 @@
           "y": 96
         },
         {
-          "x": 64.81257,
+          "x": 64.81259,
           "y": 96
         },
         {
@@ -149,11 +149,11 @@
           "y": 96
         },
         {
-          "x": 24.443243,
+          "x": 24.443266,
           "y": 96
         },
         {
-          "x": 14.680362,
+          "x": 14.680339,
           "y": 96
         },
         {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 5702a8c..69a6f2a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -348,7 +348,8 @@
     fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
+            whenever(keyguardTransitionInteractor
+                .transition(Edge.create(to = AOD)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToAodTransition(this)
@@ -369,7 +370,8 @@
     fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+            whenever(keyguardTransitionInteractor
+                .transition(Edge.create(to = LOCKSCREEN)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToLockscreenTransition(this)
@@ -390,7 +392,8 @@
     fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
+            whenever(keyguardTransitionInteractor
+                .transition(Edge.create(to = AOD)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToAodTransition(this)
@@ -411,7 +414,8 @@
     fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+            whenever(keyguardTransitionInteractor
+                .transition(Edge.create(to = LOCKSCREEN)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToLockscreenTransition(this)
@@ -432,7 +436,8 @@
     fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() =
         runBlocking(IMMEDIATE) {
             val transitionStep = MutableStateFlow(TransitionStep())
-            whenever(keyguardTransitionInteractor.transitionStepsToState(DOZING))
+            whenever(keyguardTransitionInteractor
+                .transition(Edge.create(to = DOZING)))
                 .thenReturn(transitionStep)
 
             val job = underTest.listenForAnyStateToDozingTransition(this)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
index 3afca59..336183d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt
@@ -18,26 +18,25 @@
 
 import android.testing.AndroidTestingRunner
 import android.view.View
-import android.view.ViewGroup
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
 import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.unfold.FakeUnfoldTransitionProvider
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 /**
  * Translates items away/towards the hinge when the device is opened/closed. This is controlled by
@@ -47,11 +46,16 @@
 @RunWith(AndroidTestingRunner::class)
 class KeyguardUnfoldTransitionTest : SysuiTestCase() {
 
-    @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
+    private val kosmos = Kosmos()
 
-    @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
+    private val progressProvider: FakeUnfoldTransitionProvider =
+        kosmos.fakeUnfoldTransitionProgressProvider
 
-    @Mock private lateinit var parent: ViewGroup
+    @Mock
+    private lateinit var keyguardRootView: KeyguardRootView
+
+    @Mock
+    private lateinit var notificationShadeWindowView: NotificationShadeWindowView
 
     @Mock private lateinit var statusBarStateController: StatusBarStateController
 
@@ -66,13 +70,15 @@
         xTranslationMax =
             context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
 
-        underTest = KeyguardUnfoldTransition(context, statusBarStateController, progressProvider)
+        underTest = KeyguardUnfoldTransition(
+            context, keyguardRootView, notificationShadeWindowView,
+            statusBarStateController, progressProvider
+        )
 
-        underTest.setup(parent)
+        underTest.setup()
         underTest.statusViewCentered = false
 
-        verify(progressProvider).addCallback(capture(progressListenerCaptor))
-        progressListener = progressListenerCaptor.value
+        progressListener = progressProvider
     }
 
     @Test
@@ -81,7 +87,9 @@
         underTest.statusViewCentered = true
 
         val view = View(context)
-        whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view)
+        whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(
+            view
+        )
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
@@ -101,7 +109,9 @@
         whenever(statusBarStateController.getState()).thenReturn(SHADE)
 
         val view = View(context)
-        whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view)
+        whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(
+            view
+        )
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
@@ -121,7 +131,10 @@
         whenever(statusBarStateController.getState()).thenReturn(KEYGUARD)
 
         val view = View(context)
-        whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view)
+        whenever(
+            notificationShadeWindowView
+                .findViewById<View>(R.id.lockscreen_clock_view_large)
+        ).thenReturn(view)
 
         progressListener.onTransitionStarted()
         assertThat(view.translationX).isZero()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
index 25a87b8..9580139 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
@@ -22,10 +22,6 @@
 import com.android.keyguard.LockIconView.ICON_LOCK
 import com.android.systemui.doze.util.getBurnInOffset
 import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
-import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.util.mockito.whenever
 import kotlinx.coroutines.Dispatchers
@@ -104,7 +100,7 @@
 
             // WHEN dozing updates
             mUnderTest.mIsDozingCallback.accept(true)
-            mUnderTest.mDozeTransitionCallback.accept(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+            mUnderTest.mDozeTransitionCallback.accept(1f)
 
             // THEN the view's translation is updated to use the AoD burn-in offsets
             verify(mLockIconView).setTranslationY(burnInOffset.toFloat())
@@ -113,7 +109,7 @@
 
             // WHEN the device is no longer dozing
             mUnderTest.mIsDozingCallback.accept(false)
-            mUnderTest.mDozeTransitionCallback.accept(TransitionStep(AOD, LOCKSCREEN, 0f, FINISHED))
+            mUnderTest.mDozeTransitionCallback.accept(0f)
 
             // THEN the view is updated to NO translation (no burn-in offsets anymore)
             verify(mLockIconView).setTranslationY(0f)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index d267ad4..54a14a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -39,6 +39,7 @@
 import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -1182,6 +1183,24 @@
     }
 
     @Test
+    public void testUpdateOverlayProviderViews_PendingConfigChange() {
+        final DecorProvider cutout = new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP);
+        spyOn(cutout);
+        doNothing().when(cutout).onReloadResAndMeasure(any(), anyInt(), anyInt(), anyInt(), any());
+        mMockCutoutList.add(cutout);
+        mScreenDecorations.start();
+        doCallRealMethod().when(mScreenDecorations).updateOverlayProviderViews(any());
+
+        mScreenDecorations.mPendingConfigChange = true;
+        mScreenDecorations.updateOverlayProviderViews(null /* filterIds */);
+        verify(cutout, never()).onReloadResAndMeasure(any(), anyInt(), anyInt(), anyInt(), any());
+
+        mScreenDecorations.mPendingConfigChange = false;
+        mScreenDecorations.updateOverlayProviderViews(null /* filterIds */);
+        verify(cutout).onReloadResAndMeasure(any(), anyInt(), anyInt(), anyInt(), any());
+    }
+
+    @Test
     public void testSupportHwcLayer_SwitchFrom_NotSupport() {
         setupResources(0 /* radius */, 10 /* radiusTop */, 20 /* radiusBottom */,
                 null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
index deacac3..1ce21e77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.accessibility
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
@@ -34,7 +34,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class AccessibilityLoggerTest : SysuiTestCase() {
     @JvmField @Rule val mockito = MockitoJUnit.rule()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
index 9cb4fb3..cb8cfc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
@@ -20,10 +20,10 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.hardware.display.DisplayManager;
-import android.testing.AndroidTestingRunner;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -33,7 +33,7 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class DisplayIdIndexSupplierTest extends SysuiTestCase {
 
     private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
index 5bc9aa4..cbd535b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/FullscreenMagnificationControllerTest.java
@@ -21,8 +21,10 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
-import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -34,8 +36,12 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
+import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.Display;
+import android.view.IRotationWatcher;
+import android.view.IWindowManager;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
@@ -55,6 +61,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -73,9 +81,12 @@
     private ValueAnimator mShowHideBorderAnimator;
     private SurfaceControl.Transaction mTransaction;
     private TestableWindowManager mWindowManager;
+    @Mock
+    private IWindowManager mIWindowManager;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         getInstrumentation().runOnMainSync(() -> mSurfaceControlViewHost =
                 spy(new SurfaceControlViewHost(mContext, mContext.getDisplay(),
                         new InputTransferToken(), "FullscreenMagnification")));
@@ -88,9 +99,11 @@
         mShowHideBorderAnimator = spy(newNullTargetObjectAnimator());
         mFullscreenMagnificationController = new FullscreenMagnificationController(
                 mContext,
+                mContext.getMainThreadHandler(),
                 mContext.getMainExecutor(),
                 mContext.getSystemService(AccessibilityManager.class),
                 mContext.getSystemService(WindowManager.class),
+                mIWindowManager,
                 scvhSupplier,
                 mTransaction,
                 mShowHideBorderAnimator);
@@ -104,7 +117,8 @@
     }
 
     @Test
-    public void enableFullscreenMagnification_visibleBorder() throws InterruptedException {
+    public void enableFullscreenMagnification_visibleBorder()
+            throws InterruptedException, RemoteException {
         CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
         CountDownLatch animationEndLatch = new CountDownLatch(1);
         mTransaction.addTransactionCommittedListener(
@@ -119,17 +133,21 @@
                 //Enable fullscreen magnification
                 mFullscreenMagnificationController
                         .onFullscreenMagnificationActivationChanged(true));
-        assertTrue("Failed to wait for transaction committed",
-                transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
-        assertTrue("Failed to wait for animation to be finished",
-                animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertWithMessage("Failed to wait for transaction committed")
+                .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+                .isTrue();
+        assertWithMessage("Failed to wait for animation to be finished")
+                .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+                .isTrue();
         verify(mShowHideBorderAnimator).start();
+        verify(mIWindowManager)
+                .watchRotation(any(IRotationWatcher.class), eq(Display.DEFAULT_DISPLAY));
         assertThat(mSurfaceControlViewHost.getView().isVisibleToUser()).isTrue();
     }
 
     @Test
     public void disableFullscreenMagnification_reverseAnimationAndReleaseScvh()
-            throws InterruptedException {
+            throws InterruptedException, RemoteException {
         CountDownLatch transactionCommittedLatch = new CountDownLatch(1);
         CountDownLatch enableAnimationEndLatch = new CountDownLatch(1);
         CountDownLatch disableAnimationEndLatch = new CountDownLatch(1);
@@ -149,11 +167,12 @@
                 //Enable fullscreen magnification
                 mFullscreenMagnificationController
                         .onFullscreenMagnificationActivationChanged(true));
-        assertTrue("Failed to wait for transaction committed",
-                transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
-        assertTrue("Failed to wait for enabling animation to be finished",
-                enableAnimationEndLatch.await(
-                        ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertWithMessage("Failed to wait for transaction committed")
+                .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+                .isTrue();
+        assertWithMessage("Failed to wait for enabling animation to be finished")
+                .that(enableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+                .isTrue();
         verify(mShowHideBorderAnimator).start();
 
         getInstrumentation().runOnMainSync(() ->
@@ -161,11 +180,12 @@
                 mFullscreenMagnificationController
                         .onFullscreenMagnificationActivationChanged(false));
 
-        assertTrue("Failed to wait for disabling animation to be finished",
-                disableAnimationEndLatch.await(
-                        ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertWithMessage("Failed to wait for disabling animation to be finished")
+                .that(disableAnimationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+                .isTrue();
         verify(mShowHideBorderAnimator).reverse();
         verify(mSurfaceControlViewHost).release();
+        verify(mIWindowManager).removeRotationWatcher(any(IRotationWatcher.class));
     }
 
     @Test
@@ -188,10 +208,12 @@
                 () -> mFullscreenMagnificationController
                             .onFullscreenMagnificationActivationChanged(true));
 
-        assertTrue("Failed to wait for transaction committed",
-                transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
-        assertTrue("Failed to wait for animation to be finished",
-                animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertWithMessage("Failed to wait for transaction committed")
+                .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+                .isTrue();
+        assertWithMessage("Failed to wait for animation to be finished")
+                .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+                        .isTrue();
         verify(mShowHideBorderAnimator).reverse();
     }
 
@@ -212,10 +234,12 @@
                 //Enable fullscreen magnification
                 mFullscreenMagnificationController
                         .onFullscreenMagnificationActivationChanged(true));
-        assertTrue("Failed to wait for transaction committed",
-                transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
-        assertTrue("Failed to wait for animation to be finished",
-                animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertWithMessage("Failed to wait for transaction committed")
+                .that(transactionCommittedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS))
+                .isTrue();
+        assertWithMessage("Failed to wait for animation to be finished")
+                .that(animationEndLatch.await(ANIMATION_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+                .isTrue();
         final Rect testWindowBounds = new Rect(
                 mWindowManager.getCurrentWindowMetrics().getBounds());
         testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index 3164f8e..361a945 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -37,14 +37,15 @@
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Display;
+import android.view.IWindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IMagnificationConnection;
 import android.view.accessibility.IMagnificationConnectionCallback;
 import android.view.accessibility.IRemoteMagnificationAnimationCallback;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.Flags;
@@ -67,7 +68,7 @@
  * {@link Magnification}
  */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class IMagnificationConnectionTest extends SysuiTestCase {
 
@@ -99,6 +100,8 @@
     private SecureSettings mSecureSettings;
     @Mock
     private AccessibilityLogger mA11yLogger;
+    @Mock
+    private IWindowManager mIWindowManager;
 
     private IMagnificationConnection mIMagnificationConnection;
     private Magnification mMagnification;
@@ -117,9 +120,10 @@
         mTestableLooper = TestableLooper.get(this);
         assertNotNull(mTestableLooper);
         mMagnification = new Magnification(getContext(),
-                mTestableLooper.getLooper(), getContext().getMainExecutor(), mCommandQueue,
+                mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
                 mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
-                mDisplayTracker, getContext().getSystemService(DisplayManager.class), mA11yLogger);
+                mDisplayTracker, getContext().getSystemService(DisplayManager.class),
+                mA11yLogger, mIWindowManager);
         mMagnification.mWindowMagnificationControllerSupplier =
                 new FakeWindowMagnificationControllerSupplier(
                         mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
index ad02179..7b06dd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
@@ -26,11 +26,11 @@
 
 import android.os.Handler;
 import android.os.SystemClock;
-import android.testing.AndroidTestingRunner;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -45,7 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class MagnificationGestureDetectorTest extends SysuiTestCase {
 
     private static final float ACTION_DOWN_X = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 1a88545..5be1180 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -58,7 +58,6 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Choreographer;
 import android.view.MotionEvent;
@@ -71,6 +70,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.ImageView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -90,7 +90,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class MagnificationModeSwitchTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
index 9eead6a..d0f8e78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
@@ -22,9 +22,9 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.pm.ActivityInfo;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -40,7 +40,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 /** Tests the MagnificationSettingsController. */
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class MagnificationSettingsControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index bbdd805..17b7e21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -40,13 +40,14 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Display;
+import android.view.IWindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IMagnificationConnection;
 import android.view.accessibility.IMagnificationConnectionCallback;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -64,7 +65,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class MagnificationTest extends SysuiTestCase {
 
@@ -93,6 +94,8 @@
     private MagnificationSettingsController mMagnificationSettingsController;
     @Mock
     private AccessibilityLogger mA11yLogger;
+    @Mock
+    private IWindowManager mIWindowManager;
 
     @Before
     public void setUp() throws Exception {
@@ -122,10 +125,10 @@
 
         mCommandQueue = new CommandQueue(getContext(), mDisplayTracker);
         mMagnification = new Magnification(getContext(),
-                getContext().getMainThreadHandler(), getContext().getMainExecutor(),
+                getContext().getMainThreadHandler(), mContext.getMainExecutor(),
                 mCommandQueue, mModeSwitchesController,
                 mSysUiState, mOverviewProxyService, mSecureSettings, mDisplayTracker,
-                getContext().getSystemService(DisplayManager.class), mA11yLogger);
+                getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager);
         mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
                 mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
         mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
index e81613e..8f9b7c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
@@ -27,12 +27,12 @@
 
 import android.content.Context;
 import android.graphics.Point;
-import android.testing.AndroidTestingRunner;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -45,7 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class MirrorWindowControlTest extends SysuiTestCase {
 
     @Mock WindowManager mWindowManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index 3c97423..6e94297 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -21,11 +21,11 @@
 import android.content.pm.ActivityInfo;
 import android.hardware.display.DisplayManager;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Display;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 /** Tests the ModeSwitchesController. */
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class ModeSwitchesControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
index 9c601a8..9222fc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SecureSettingsContentObserverTest.java
@@ -21,8 +21,8 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -34,7 +34,7 @@
 import org.mockito.Mockito;
 
 /** Test for {@link SecureSettingsContentObserver}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SecureSettingsContentObserverTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
index c674294..f46b2f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
@@ -28,10 +28,10 @@
 import android.os.RemoteException;
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.KeyEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -55,7 +55,7 @@
 
 @TestableLooper.RunWithLooper
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class SystemActionsTest extends SysuiTestCase {
     @Mock
     private UserTracker mUserTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index cb42078..f57003e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -72,7 +72,6 @@
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
 import android.text.TextUtils;
@@ -90,6 +89,7 @@
 import android.view.accessibility.IRemoteMagnificationAnimationCallback;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -125,7 +125,7 @@
 
 @LargeTest
 @TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
 public class WindowMagnificationControllerTest extends SysuiTestCase {
 
@@ -1511,4 +1511,4 @@
             });
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
index 01e4d58..e272682 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
@@ -71,7 +71,6 @@
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
 import android.text.TextUtils;
@@ -94,6 +93,7 @@
 import android.window.InputTransferToken;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 
 import com.android.systemui.Flags;
@@ -129,7 +129,7 @@
 
 @LargeTest
 @TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RequiresFlagsEnabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
 public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
index 93c0eea..ad9053a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
@@ -23,10 +23,10 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Size;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -37,7 +37,7 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 138fed2..003f7e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -47,7 +47,6 @@
 import android.graphics.Rect;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.view.ViewGroup;
@@ -59,6 +58,7 @@
 import android.widget.LinearLayout;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -77,7 +77,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class WindowMagnificationSettingsTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
index aff52f5..c4a92bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
@@ -18,8 +18,8 @@
 
 package com.android.systemui.accessibility.data.repository
 
-import android.testing.AndroidTestingRunner
 import android.view.accessibility.AccessibilityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -37,7 +37,7 @@
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class AccessibilityRepositoryTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 095c945..b71739a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -30,11 +30,11 @@
 import android.hardware.display.DisplayManager;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -59,7 +59,7 @@
 import org.mockito.junit.MockitoRule;
 
 /** Test for {@link AccessibilityFloatingMenuController}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
index 630db62..b08f97a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
@@ -21,10 +21,10 @@
 import static org.mockito.Mockito.when;
 
 import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
 import android.view.LayoutInflater;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
@@ -43,7 +43,7 @@
 
 /** Tests for {@link AccessibilityTargetAdapter}. */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class AccessibilityTargetAdapterTest extends SysuiTestCase {
     @Mock
     private AccessibilityTarget mAccessibilityTarget;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
index 4b87588..5b2afe7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
@@ -20,10 +20,10 @@
 
 import static org.mockito.Mockito.mock;
 
-import android.testing.AndroidTestingRunner;
 import android.text.SpannableStringBuilder;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -37,7 +37,7 @@
 
 /** Tests for {@link AnnotationLinkSpan}. */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class AnnotationLinkSpanTest extends SysuiTestCase {
 
     private AnnotationLinkSpan.LinkInfo mLinkInfo;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index abc95bc..19b2700 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -22,11 +22,11 @@
 import android.annotation.NonNull;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.Flags;
@@ -46,7 +46,7 @@
 
 /** Tests for {@link DragToInteractAnimationController}. */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class DragToInteractAnimationControllerTest extends SysuiTestCase {
     private DragToInteractAnimationController mDragToInteractAnimationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
index 34a2e87..b597737 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
@@ -19,11 +19,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
 import android.widget.TextView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
 
 /** Tests for {@link MenuEduTooltipView}. */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class MenuEduTooltipViewTest extends SysuiTestCase {
     private MenuViewAppearance mMenuViewAppearance;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
index 1faa8ac..24f3a29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -25,9 +25,9 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.testing.AndroidTestingRunner;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -47,7 +47,7 @@
 import java.util.Locale;
 
 /** Tests for {@link MenuInfoRepository}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MenuInfoRepositoryTest extends SysuiTestCase {
     @Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 1f7d033..c5509ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -30,7 +30,6 @@
 import android.graphics.Rect;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -39,6 +38,7 @@
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.Flags;
@@ -60,7 +60,7 @@
 /** Tests for {@link MenuItemAccessibilityDelegate}. */
 @SmallTest
 @TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class MenuItemAccessibilityDelegateTest extends SysuiTestCase {
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 9e8c6b3..4373c88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -30,7 +30,6 @@
 
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
 import android.view.WindowManager;
@@ -38,6 +37,7 @@
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
@@ -62,7 +62,7 @@
 import java.util.List;
 
 /** Tests for {@link MenuListViewTouchHandler}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class MenuListViewTouchHandlerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
index 9dd337e..2746fef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuNotificationFactoryTest.java
@@ -19,8 +19,8 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.Notification;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -29,7 +29,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MenuNotificationFactoryTest extends SysuiTestCase {
     private MenuNotificationFactory mMenuNotificationFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index 31824ec..bd1a7f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -27,7 +27,6 @@
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.view.ViewGroup;
@@ -36,6 +35,7 @@
 import android.view.WindowMetrics;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -50,7 +50,7 @@
 import org.mockito.junit.MockitoRule;
 
 /** Tests for {@link MenuViewLayerController}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class MenuViewLayerControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 05d7560..38095c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -61,7 +61,6 @@
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArraySet;
 import android.view.View;
@@ -72,6 +71,7 @@
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.accessibility.common.ShortcutConstants;
@@ -101,7 +101,7 @@
 import java.util.List;
 
 /** Tests for {@link MenuViewLayer}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class MenuViewLayerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index f6288b6..103449b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -29,11 +29,11 @@
 import android.graphics.Rect;
 import android.graphics.drawable.GradientDrawable;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.Flags;
@@ -53,7 +53,7 @@
 import org.mockito.junit.MockitoRule;
 
 /** Tests for {@link MenuView}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class MenuViewTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
index 05f306b..8fb71fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -29,7 +29,7 @@
 
 /** Tests for {@link Position}. */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class PositionTest extends SysuiTestCase {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
index d77a80a..f67e8d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java
@@ -20,8 +20,8 @@
 
 import android.os.Handler;
 import android.os.Looper;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
 
 /** Tests for {@link RadiiAnimator}. */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class RadiiAnimatorTest extends SysuiTestCase {
     float[] mResultRadii = new float[RadiiAnimator.RADII_COUNT];
     final AtomicBoolean mAnimationStarted = new AtomicBoolean(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index e371b39..0bd00fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -18,12 +18,12 @@
 import android.content.res.Configuration
 import android.os.Handler
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
 import android.view.ViewGroup
 import android.widget.Button
 import android.widget.SeekBar
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -58,7 +58,7 @@
 
 /** Tests for [FontScalingDialogDelegate]. */
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class FontScalingDialogDelegateTest : SysuiTestCase() {
     private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java
new file mode 100644
index 0000000..51f6cdb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.accessibility.hearingaid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class HearingDevicesCheckerTest extends SysuiTestCase {
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+
+    private final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
+    @Mock
+    private LocalBluetoothAdapter mLocalBluetoothAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager;
+    @Mock
+    private CachedBluetoothDevice mCachedDevice;
+    @Mock
+    private BluetoothDevice mDevice;
+    private HearingDevicesChecker mDevicesChecker;
+
+    @Before
+    public void setUp() {
+        when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
+        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
+                mCachedBluetoothDeviceManager);
+        when(mCachedBluetoothDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
+        when(mCachedDevice.getDevice()).thenReturn(mDevice);
+        when(mDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+                null);
+
+        mDevicesChecker = new HearingDevicesChecker(mContext, mLocalBluetoothManager);
+    }
+
+    @Test
+    public void isAnyPairedHearingDevice_bluetoothDisable_returnFalse() {
+        when(mLocalBluetoothAdapter.isEnabled()).thenReturn(false);
+
+        assertThat(mDevicesChecker.isAnyPairedHearingDevice()).isFalse();
+    }
+
+    @Test
+    public void isAnyActiveHearingDevice_bluetoothDisable_returnFalse() {
+        when(mLocalBluetoothAdapter.isEnabled()).thenReturn(false);
+
+        assertThat(mDevicesChecker.isAnyActiveHearingDevice()).isFalse();
+    }
+
+    @Test
+    public void isAnyPairedHearingDevice_hearingAidBonded_returnTrue() {
+        when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true);
+        when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
+        when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        mCachedDevices.add(mCachedDevice);
+
+        assertThat(mDevicesChecker.isAnyPairedHearingDevice()).isTrue();
+    }
+
+    @Test
+    public void isAnyActiveHearingDevice_hearingAidActiveAndConnected_returnTrue() {
+        when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true);
+        when(mCachedDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
+        when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice.isConnected()).thenReturn(true);
+        when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
+        mCachedDevices.add(mCachedDevice);
+
+        assertThat(mDevicesChecker.isAnyActiveHearingDevice()).isTrue();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 0db0de2..8f7dc7cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -39,11 +39,11 @@
 import android.os.Handler;
 import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.LinearLayout;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.bluetooth.BluetoothEventManager;
@@ -78,7 +78,7 @@
 import java.util.List;
 
 /** Tests for {@link HearingDevicesDialogDelegate}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
index e9c742d..09aa2868 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
@@ -22,19 +22,17 @@
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothDevice;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
-import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.animation.Expandable;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -44,11 +42,8 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /** Tests for {@link HearingDevicesDialogManager}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class HearingDevicesDialogManagerTest extends SysuiTestCase {
@@ -56,7 +51,8 @@
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
 
-    private final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
+    private final FakeExecutor mMainExecutor = new FakeExecutor(new FakeSystemClock());
+    private final FakeExecutor mBackgroundExecutor = new FakeExecutor(new FakeSystemClock());
     @Mock
     private Expandable mExpandable;
     @Mock
@@ -68,13 +64,7 @@
     @Mock
     private SystemUIDialog mDialog;
     @Mock
-    private LocalBluetoothManager mLocalBluetoothManager;
-    @Mock
-    private LocalBluetoothAdapter mLocalBluetoothAdapter;
-    @Mock
-    private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager;
-    @Mock
-    private CachedBluetoothDevice mCachedDevice;
+    private HearingDevicesChecker mDevicesChecker;
 
     private HearingDevicesDialogManager mManager;
 
@@ -82,36 +72,35 @@
     public void setUp() {
         when(mDialogFactory.create(anyBoolean())).thenReturn(mDialogDelegate);
         when(mDialogDelegate.createDialog()).thenReturn(mDialog);
-        when(mLocalBluetoothManager.getBluetoothAdapter()).thenReturn(mLocalBluetoothAdapter);
-        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
-                mCachedBluetoothDeviceManager);
-        when(mCachedBluetoothDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
 
         mManager = new HearingDevicesDialogManager(
                 mDialogTransitionAnimator,
                 mDialogFactory,
-                mLocalBluetoothManager
+                mDevicesChecker,
+                mBackgroundExecutor,
+                mMainExecutor
         );
     }
 
     @Test
-    public void showDialog_bluetoothDisable_showPairNewDeviceTrue() {
-        when(mLocalBluetoothAdapter.isEnabled()).thenReturn(false);
+    public void showDialog_existHearingDevice_showPairNewDeviceFalse() {
+        when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(true);
 
         mManager.showDialog(mExpandable);
+        mBackgroundExecutor.runAllReady();
+        mMainExecutor.runAllReady();
 
-        verify(mDialogFactory).create(eq(true));
+        verify(mDialogFactory).create(eq(/* showPairNewDevice= */ false));
     }
 
     @Test
-    public void showDialog_containsHearingAid_showPairNewDeviceFalse() {
-        when(mLocalBluetoothAdapter.isEnabled()).thenReturn(true);
-        when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
-        when(mCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-        mCachedDevices.add(mCachedDevice);
+    public void showDialog_noHearingDevice_showPairNewDeviceTrue() {
+        when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(false);
 
         mManager.showDialog(mExpandable);
+        mBackgroundExecutor.runAllReady();
+        mMainExecutor.runAllReady();
 
-        verify(mDialogFactory).create(eq(false));
+        verify(mDialogFactory).create(eq(/* showPairNewDevice= */ true));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
index d16db65..9359adf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
@@ -20,9 +20,9 @@
 
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -41,7 +41,7 @@
 import java.util.List;
 
 /** Tests for {@link HearingDevicesListAdapter}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class HearingDevicesListAdapterTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
index 7172923..17ce1dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
@@ -28,9 +28,9 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -48,7 +48,7 @@
 /**
  * Tests for {@link HearingDevicesToolItemParser}.
  */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class HearingDevicesToolItemParserTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
index 2f4999b..8fca557 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
@@ -24,13 +24,13 @@
 import static org.mockito.Mockito.when;
 
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Choreographer;
 import android.view.GestureDetector;
 import android.view.InputEvent;
 import android.view.MotionEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.Flags;
@@ -50,7 +50,7 @@
  * A test suite for exercising {@link InputSession}.
  */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper()
 public class InputSessionTest extends SysuiTestCase {
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index 358e8cb..4118c90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -36,7 +36,6 @@
 import android.hardware.display.DisplayManager;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.GestureDetector;
 import android.view.IWindowManager;
@@ -50,6 +49,7 @@
 import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.Flags;
@@ -79,7 +79,7 @@
 import java.util.stream.Stream;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class TouchMonitorTest extends SysuiTestCase {
     private KosmosJavaAdapter mKosmos;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 70a544c..9aaf295 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -9,7 +9,6 @@
 import android.graphics.Rect
 import android.os.Looper
 import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.IRemoteAnimationFinishedCallback
 import android.view.RemoteAnimationAdapter
@@ -20,6 +19,7 @@
 import android.widget.LinearLayout
 import android.window.RemoteTransition
 import android.window.TransitionFilter
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.shared.Flags
@@ -49,7 +49,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class ActivityTransitionAnimatorTest : SysuiTestCase() {
     private val transitionContainer = LinearLayout(mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
index e3be3822..37f549a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.animation
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import androidx.core.animation.doOnEnd
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -28,7 +28,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @RunWithLooper
 @FlakyTest(bugId = 302149604)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
index e14762cd..a60fb76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
@@ -5,7 +5,6 @@
 import android.graphics.Color
 import android.graphics.drawable.ColorDrawable
 import android.os.Bundle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.ViewUtils
 import android.view.View
@@ -14,6 +13,7 @@
 import android.view.WindowManager
 import android.widget.FrameLayout
 import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.Cuj
 import com.android.internal.policy.DecorView
@@ -39,7 +39,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class DialogTransitionAnimatorTest : SysuiTestCase() {
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index 5e1a8e1..ec42b7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -20,14 +20,14 @@
 import android.graphics.fonts.Font
 import android.graphics.fonts.FontVariationAxis
 import android.graphics.text.TextRunShaper
-import android.testing.AndroidTestingRunner
+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
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class FontInterpolatorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
index 070cad7..b0f81c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
@@ -1,6 +1,6 @@
 package com.android.systemui.animation
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import junit.framework.Assert
@@ -12,7 +12,7 @@
 private const val TAG_OPSZ = "opsz"
 private const val TAG_ROND = "ROND"
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class FontVariationUtilsTest : SysuiTestCase() {
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
index 42fcd54..e492c63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
@@ -17,10 +17,10 @@
 package com.android.systemui.animation
 
 import android.os.HandlerThread
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.SysuiTestCase
@@ -31,7 +31,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class GhostedViewTransitionAnimatorControllerTest : SysuiTestCase() {
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
index 263d375..6ba1715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
@@ -19,10 +19,10 @@
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
 import android.graphics.Typeface
-import android.testing.AndroidTestingRunner
 import android.text.Layout
 import android.text.StaticLayout
 import android.text.TextPaint
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -38,7 +38,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class TextAnimatorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
index f6fcd16..cca5f35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
@@ -22,12 +22,12 @@
 import android.graphics.Typeface
 import android.graphics.fonts.Font
 import android.graphics.fonts.FontFamily
-import android.testing.AndroidTestingRunner
 import android.text.Layout
 import android.text.StaticLayout
 import android.text.TextDirectionHeuristic
 import android.text.TextDirectionHeuristics
 import android.text.TextPaint
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -62,7 +62,7 @@
     typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 700").build().toTypeface()
 }
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class TextInterpolatorTest : SysuiTestCase() {
     lateinit var typefaceCache: TypefaceVariantCache
@@ -330,4 +330,4 @@
         Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!!
 
 private fun TextInterpolator.toBitmap(width: Int, height: Int) =
-        Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
\ No newline at end of file
+        Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
index c2e6db3..a8c3af9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
@@ -1,12 +1,12 @@
 package com.android.systemui.animation
 
 import android.animation.ObjectAnimator
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
 import android.widget.LinearLayout
 import android.widget.RelativeLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.app.animation.Interpolators
 import com.android.systemui.SysuiTestCase
@@ -22,7 +22,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class
 ViewHierarchyAnimatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
index 0ed84ea..4809d0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
@@ -2,6 +2,7 @@
 
 import android.util.DisplayMetrics
 import android.window.BackEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.dpToPx
@@ -9,12 +10,11 @@
 import junit.framework.TestCase.assertEquals
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 private data class BackInput(val progressX: Float, val progressY: Float, val edge: Int)
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BackAnimationSpecTest : SysuiTestCase() {
     private var displayMetrics =
         DisplayMetrics().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
index 44a5467..d898d1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.animation.back
 
 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.util.mockito.mock
@@ -8,13 +9,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BackTransformationTest : SysuiTestCase() {
     private val targetView: View = mock()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
index 314abda..9548e29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
@@ -2,6 +2,7 @@
 
 import android.util.DisplayMetrics
 import android.window.BackEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.argumentCaptor
@@ -10,11 +11,10 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mockito.verify
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class OnBackAnimationCallbackExtensionTest : SysuiTestCase() {
     private val onBackProgress: (BackTransformation) -> Unit = mock()
     private val onBackStart: (BackEvent) -> Unit = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 0d464cf..476d6e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -49,9 +49,9 @@
 import android.media.AudioRecordingConfiguration;
 import android.os.Looper;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
@@ -75,7 +75,7 @@
 import java.util.Map;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class AppOpsControllerTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
index 4d582ab..828d367 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
@@ -21,8 +21,8 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class DisplayUtilsTest extends SysuiTestCase {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
index 3c073d5..2bd0976 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
@@ -17,9 +17,9 @@
 
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.widget.ImageView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.flags.Flags.FLAG_NEW_STATUS_BAR_ICONS
 import com.android.systemui.res.R
@@ -33,7 +33,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class BatteryMeterViewTest : SysuiTestCase() {
 
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 7c03d78..6dc4b10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -19,9 +19,9 @@
 import android.graphics.Point
 import android.hardware.biometrics.BiometricSourceType
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.util.DisplayMetrics
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -67,7 +67,7 @@
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class AuthRippleControllerTest : SysuiTestCase() {
     private lateinit var staticMockSession: MockitoSession
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
index d2c6957..197cb84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
@@ -33,9 +33,9 @@
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -53,7 +53,7 @@
 import java.util.concurrent.ExecutionException;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BiometricNotificationDialogFactoryTest extends SysuiTestCase {
     @Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
index a279d3e..20d9433 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
@@ -36,9 +36,9 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Handler;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -58,7 +58,7 @@
 import java.util.Optional;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BiometricNotificationServiceTest extends SysuiTestCase {
     @Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
index 5b6aee6..d26ccbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
@@ -23,6 +23,7 @@
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.view.Surface;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -33,11 +34,10 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class UdfpsUtilsTest extends SysuiTestCase {
     @Rule
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
index b3e845f..d215047 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
@@ -29,6 +29,7 @@
 import android.hardware.biometrics.events.AuthenticationAcquiredInfo
 import android.hardware.biometrics.events.AuthenticationStartedInfo
 import android.hardware.biometrics.events.AuthenticationStoppedInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.shared.model.AuthenticationReason
@@ -46,7 +47,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
@@ -54,7 +54,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BiometricStatusRepositoryTest : SysuiTestCase() {
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
     @Mock private lateinit var biometricManager: BiometricManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
index eae953e..d9b7161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
@@ -21,6 +21,7 @@
 import android.view.Display.DEFAULT_DISPLAY
 import android.view.DisplayInfo
 import android.view.Surface
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.DisplayStateRepository
@@ -42,12 +43,11 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mockito.spy
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DisplayStateRepositoryTest : SysuiTestCase() {
     private val display = mock<Display>()
     private val testScope = TestScope(StandardTestDispatcher())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
index f5e96c9..9c11405 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
@@ -26,6 +26,7 @@
 import android.hardware.face.FaceManager
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.face.IFaceAuthenticatorsRegisteredCallback
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.shared.model.LockoutMode
@@ -46,7 +47,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
@@ -57,7 +57,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class FacePropertyRepositoryImplTest : SysuiTestCase() {
     companion object {
         private const val LOGICAL_CAMERA_ID_OUTER_FRONT = "0"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
index 6391986..0209ab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
@@ -19,6 +19,7 @@
 import android.database.ContentObserver
 import android.os.Handler
 import android.provider.Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -35,7 +36,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
@@ -47,7 +47,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class FaceSettingsRepositoryImplTest : SysuiTestCase() {
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
index 7808c41..ff5a419f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
@@ -23,6 +23,7 @@
 import android.hardware.fingerprint.FingerprintSensorProperties
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
@@ -38,7 +39,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
@@ -47,7 +47,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class FingerprintRepositoryImplTest : SysuiTestCase() {
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index 2682633..22971bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics.data.repository
 
 import android.hardware.biometrics.PromptInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
@@ -37,7 +38,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.verify
@@ -51,7 +51,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PromptRepositoryImplTest : SysuiTestCase() {
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
index 4cff3e6..5d2d20c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityManager
 import android.content.ComponentName
 import android.hardware.biometrics.BiometricFingerprintConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.app.activityTaskManager
 import com.android.systemui.SysuiTestCase
@@ -37,13 +38,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mockito
 import org.mockito.Mockito.`when`
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BiometricStatusInteractorImplTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private lateinit var underTest: BiometricStatusInteractorImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
index 8690d4e..4856f15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
@@ -4,6 +4,7 @@
 import android.app.admin.DevicePolicyResourcesManager
 import android.content.pm.UserInfo
 import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockscreenCredential
@@ -25,7 +26,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
@@ -38,7 +38,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class CredentialInteractorImplTest : SysuiTestCase() {
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
index 31bdde2..f40b6b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.biometrics.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -22,14 +23,13 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DisplayStateInteractorImplTest : SysuiTestCase() {
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
index 3f83ce3..a58efd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
@@ -19,6 +19,7 @@
 import android.hardware.biometrics.AuthenticateOptions
 import android.hardware.biometrics.IBiometricContextListener
 import android.hardware.biometrics.IBiometricContextListener.FoldState
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -40,12 +41,11 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.junit.MockitoJUnit
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class LogContextInteractorImplTest : SysuiTestCase() {
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
index c4d0d23..5a36376 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
@@ -3,6 +3,7 @@
 import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
 import android.hardware.biometrics.PromptInfo
 import android.hardware.biometrics.PromptVerticalListContentView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
@@ -29,7 +30,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.junit.MockitoJUnit
 
 private const val USER_ID = 22
@@ -39,7 +39,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PromptCredentialInteractorTest : SysuiTestCase() {
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 6e78e33..720f207 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -22,6 +22,7 @@
 import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
 import android.hardware.biometrics.PromptInfo
 import android.hardware.biometrics.PromptVerticalListContentView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
@@ -47,13 +48,12 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.junit.MockitoJUnit
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PromptSelectorInteractorImplTest : SysuiTestCase() {
     companion object {
         private const val TITLE = "hey there"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
index 5e7adb7..3d63c5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
@@ -19,6 +19,7 @@
 import android.graphics.Rect
 import android.view.MotionEvent
 import android.view.Surface
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
@@ -34,7 +35,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Captor
@@ -44,7 +44,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class UdfpsOverlayInteractorTest : SysuiTestCase() {
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
index d10b935..08f139c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequestTest.kt
@@ -4,8 +4,10 @@
 import android.hardware.biometrics.PromptContentItemBulletedText
 import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton
 import android.hardware.biometrics.PromptVerticalListContentView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.Utils.toBitmap
 import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
 import com.android.systemui.biometrics.promptInfo
 import com.android.systemui.biometrics.shared.model.BiometricModalities
@@ -15,19 +17,19 @@
 import com.google.common.util.concurrent.MoreExecutors
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 private const val USER_ID = 2
 private const val OPERATION_ID = 8L
 private const val OP_PACKAGE_NAME = "biometric.testapp"
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BiometricPromptRequestTest : SysuiTestCase() {
 
     @Test
     fun biometricRequestFromPromptInfo() {
         val logoRes = R.drawable.ic_cake
+        val logoBitmapFromRes = context.getDrawable(logoRes).toBitmap()
         val logoDescription = "test cake"
         val title = "what"
         val subtitle = "a"
@@ -44,6 +46,7 @@
             BiometricPromptRequest.Biometric(
                 promptInfo(
                     logoRes = logoRes,
+                    logoBitmap = logoBitmapFromRes,
                     logoDescription = logoDescription,
                     title = title,
                     subtitle = subtitle,
@@ -56,7 +59,8 @@
                 OP_PACKAGE_NAME,
             )
 
-        assertThat(request.logoRes).isEqualTo(logoRes)
+        assertThat(request.logoBitmap).isNotNull()
+        assertThat(request.logoBitmap!!.sameAs(logoBitmapFromRes)).isTrue()
         assertThat(request.logoDescription).isEqualTo(logoDescription)
         assertThat(request.title).isEqualTo(title)
         assertThat(request.subtitle).isEqualTo(subtitle)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
index 74c4313..4d8fafc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/shared/model/BiometricModalitiesTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.biometrics.shared.model
 
 import android.hardware.fingerprint.FingerprintSensorProperties
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.faceSensorPropertiesInternal
@@ -24,10 +25,9 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BiometricModalitiesTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
index 95b72d5..f9bedc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
@@ -21,12 +21,13 @@
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 class BoundingBoxOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
     val underTest = BoundingBoxOverlapDetector(1f)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
index 317141b..33ddbf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
@@ -22,12 +22,13 @@
 import com.android.systemui.biometrics.EllipseOverlapDetectorParams
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 class EllipseOverlapDetectorTest(val testCase: TestCase) : SysuiTestCase() {
     val underTest =
         EllipseOverlapDetector(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
index 3e5c43a..3863b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
@@ -5,12 +5,13 @@
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 class NormalizedTouchDataTest(val testCase: TestCase) : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
index aff93bd..a4653e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
@@ -27,12 +27,13 @@
 import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 class SinglePointerTouchProcessorTest(val testCase: TestCase) : SysuiTestCase() {
     private val overlapDetector = FakeOverlapDetector()
     private val underTest = SinglePointerTouchProcessor(overlapDetector)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
index ec2b104..4238254 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
@@ -32,6 +32,7 @@
 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
@@ -61,7 +62,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.any
@@ -76,7 +76,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class SideFpsOverlayViewBinderTest : SysuiTestCase() {
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
index 9e804c1..e4c5cd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.biometrics.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
@@ -19,7 +20,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 private const val USER_ID = 9
 private const val REQUEST_ID = 9L
@@ -27,7 +27,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class CredentialViewModelTest : SysuiTestCase() {
 
     private val dispatcher = UnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
index 1b6aaab..77ddd31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
@@ -34,7 +35,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mockito.verify
@@ -42,7 +42,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DeviceEntryUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
index 278a43e..3eb2ff3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
@@ -16,16 +16,16 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.shared.model.BiometricModality
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PromptAuthStateTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
index f9b590f..81132d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.biometrics.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.shared.model.BiometricModality
@@ -23,10 +24,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PromptHistoryImplTest : SysuiTestCase() {
 
     private lateinit var history: PromptHistoryImpl
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 1167fce..93c6d9e 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
@@ -26,6 +26,7 @@
 import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.Point
+import android.graphics.Rect
 import android.graphics.drawable.BitmapDrawable
 import android.hardware.biometrics.BiometricFingerprintConstants
 import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
@@ -48,6 +49,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.UdfpsUtils
+import com.android.systemui.biometrics.Utils.toBitmap
 import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
@@ -89,11 +91,12 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.junit.MockitoJUnit
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 private const val USER_ID = 4
 private const val REQUEST_ID = 4L
@@ -105,7 +108,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCase() {
 
     @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@@ -127,11 +130,32 @@
     private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
     private val defaultLogoIconWithOverrides = context.getDrawable(R.drawable.ic_add)
     private val logoResFromApp = R.drawable.ic_cake
-    private val logoFromApp = context.getDrawable(logoResFromApp)
+    private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp)
     private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565)
     private val defaultLogoDescription = "Test Android App"
     private val logoDescriptionFromApp = "Test Cake App"
     private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
+    /** Prompt panel size padding */
+    private val smallHorizontalGuidelinePadding =
+        context.resources.getDimensionPixelSize(
+            R.dimen.biometric_prompt_land_small_horizontal_guideline_padding
+        )
+    private val udfpsHorizontalGuidelinePadding =
+        context.resources.getDimensionPixelSize(
+            R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding
+        )
+    private val udfpsHorizontalShorterGuidelinePadding =
+        context.resources.getDimensionPixelSize(
+            R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding
+        )
+    private val mediumTopGuidelinePadding =
+        context.resources.getDimensionPixelSize(
+            R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding
+        )
+    private val mediumHorizontalGuidelinePadding =
+        context.resources.getDimensionPixelSize(
+            R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding
+        )
 
     private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository
     private lateinit var promptRepository: FakePromptRepository
@@ -221,7 +245,7 @@
 
         context.setMockPackageManager(packageManager)
         val resources = context.getOrCreateTestableResources()
-        resources.addOverride(logoResFromApp, logoFromApp)
+        resources.addOverride(logoResFromApp, logoDrawableFromAppRes)
         resources.addOverride(
             R.array.biometric_dialog_package_names_for_logo_with_overrides,
             arrayOf(packageNameForLogoWithOverrides)
@@ -823,6 +847,28 @@
         assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
     }
 
+    @Test
+    fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest {
+        val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+        viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+
+        viewModel.showTemporaryError(
+            "still sad",
+            messageAfterError = "",
+            authenticateAfterError = false,
+            hapticFeedback = true,
+        )
+
+        val haptics by collectLastValue(viewModel.hapticsToPlay)
+        if (expectConfirmation) {
+            assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
+            assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+        } else {
+            assertThat(haptics?.hapticFeedbackConstant)
+                .isEqualTo(HapticFeedbackConstants.CONFIRM)
+        }
+    }
+
     private suspend fun TestScope.showTemporaryErrors(
         restart: Boolean,
         helpAfterError: String = "",
@@ -970,7 +1016,7 @@
     }
 
     @Test
-    fun authenticated_at_most_once() = runGenericTest {
+    fun authenticated_at_most_once_same_modality() = runGenericTest {
         val authenticating by collectLastValue(viewModel.isAuthenticating)
         val authenticated by collectLastValue(viewModel.isAuthenticated)
 
@@ -1032,6 +1078,38 @@
     }
 
     @Test
+    fun second_authentication_acts_as_confirmation() = runGenericTest {
+        val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+
+        viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+
+        val authenticating by collectLastValue(viewModel.isAuthenticating)
+        val authenticated by collectLastValue(viewModel.isAuthenticated)
+        val message by collectLastValue(viewModel.message)
+        val size by collectLastValue(viewModel.size)
+        val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+
+        assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
+        if (expectConfirmation) {
+            assertThat(size).isEqualTo(PromptSize.MEDIUM)
+            assertButtonsVisible(
+                cancel = true,
+                confirm = true,
+            )
+
+            if (testCase.modalities.hasSfps) {
+                viewModel.showAuthenticated(BiometricModality.Fingerprint, 0)
+                assertThat(message).isEqualTo(PromptMessage.Empty)
+                assertButtonsVisible()
+            }
+        }
+
+        assertThat(authenticating).isFalse()
+        assertThat(authenticated?.isAuthenticated).isTrue()
+        assertThat(canTryAgain).isFalse()
+    }
+
+    @Test
     fun auto_confirm_authentication_when_finger_down() = runGenericTest {
         val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
 
@@ -1249,7 +1327,7 @@
     @Test
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun descriptionOverriddenByVerticalListContentView() =
-        runGenericTest(contentView = promptContentView, description = "test description") {
+        runGenericTest(description = "test description", contentView = promptContentView) {
             val contentView by collectLastValue(viewModel.contentView)
             val description by collectLastValue(viewModel.description)
 
@@ -1261,8 +1339,8 @@
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
         runGenericTest(
-            contentView = promptContentViewWithMoreOptionsButton,
-            description = "test description"
+            description = "test description",
+            contentView = promptContentViewWithMoreOptionsButton
         ) {
             val contentView by collectLastValue(viewModel.contentView)
             val description by collectLastValue(viewModel.description)
@@ -1322,8 +1400,9 @@
     @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
     fun logo_resSetByApp() =
         runGenericTest(logoRes = logoResFromApp) {
+            val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
             val logo by collectLastValue(viewModel.logo)
-            assertThat(logo).isEqualTo(logoFromApp)
+            assertThat((logo as BitmapDrawable).bitmap.sameAs(expectedBitmap)).isTrue()
         }
 
     @Test
@@ -1366,6 +1445,142 @@
         }
 
     @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun position_bottom_rotation0() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+        val position by collectLastValue(viewModel.position)
+        assertThat(position).isEqualTo(PromptPosition.Bottom)
+    } // TODO(b/335278136): Add test for no sensor landscape
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun position_bottom_forceLarge() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+        viewModel.onSwitchToCredential()
+        val position by collectLastValue(viewModel.position)
+        assertThat(position).isEqualTo(PromptPosition.Bottom)
+    }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun position_bottom_largeScreen() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+        displayStateRepository.setIsLargeScreen(true)
+        val position by collectLastValue(viewModel.position)
+        assertThat(position).isEqualTo(PromptPosition.Bottom)
+    }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun position_right_rotation90() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+        val position by collectLastValue(viewModel.position)
+        assertThat(position).isEqualTo(PromptPosition.Right)
+    }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun position_left_rotation270() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+        val position by collectLastValue(viewModel.position)
+        assertThat(position).isEqualTo(PromptPosition.Left)
+    }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun position_top_rotation180() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+        val position by collectLastValue(viewModel.position)
+        if (testCase.modalities.hasUdfps) {
+            assertThat(position).isEqualTo(PromptPosition.Top)
+        } else {
+            assertThat(position).isEqualTo(PromptPosition.Bottom)
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun guideline_bottom() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+        val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+        assertThat(guidelineBounds).isEqualTo(Rect(0, mediumTopGuidelinePadding, 0, 0))
+    } // TODO(b/335278136): Add test for no sensor landscape
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun guideline_right() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+        val isSmall = testCase.shouldStartAsImplicitFlow
+        val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+
+        if (isSmall) {
+            assertThat(guidelineBounds).isEqualTo(Rect(-smallHorizontalGuidelinePadding, 0, 0, 0))
+        } else if (testCase.modalities.hasUdfps) {
+            assertThat(guidelineBounds).isEqualTo(Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0))
+        } else {
+            assertThat(guidelineBounds).isEqualTo(Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0))
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun guideline_right_onlyShortTitle() =
+        runGenericTest(subtitle = "") {
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+            val isSmall = testCase.shouldStartAsImplicitFlow
+            val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+
+            if (!isSmall && testCase.modalities.hasUdfps) {
+                assertThat(guidelineBounds)
+                    .isEqualTo(Rect(-udfpsHorizontalShorterGuidelinePadding, 0, 0, 0))
+            }
+        }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun guideline_left() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+        val isSmall = testCase.shouldStartAsImplicitFlow
+        val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+
+        if (isSmall) {
+            assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -smallHorizontalGuidelinePadding, 0))
+        } else if (testCase.modalities.hasUdfps) {
+            assertThat(guidelineBounds).isEqualTo(Rect(0, 0, udfpsHorizontalGuidelinePadding, 0))
+        } else {
+            assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -mediumHorizontalGuidelinePadding, 0))
+        }
+    }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun guideline_left_onlyShortTitle() =
+        runGenericTest(subtitle = "") {
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+            val isSmall = testCase.shouldStartAsImplicitFlow
+            val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+
+            if (!isSmall && testCase.modalities.hasUdfps) {
+                assertThat(guidelineBounds)
+                    .isEqualTo(Rect(0, 0, -udfpsHorizontalShorterGuidelinePadding, 0))
+            }
+        }
+
+    @Test
+    @EnableFlags(FLAG_CONSTRAINT_BP)
+    fun guideline_top() = runGenericTest {
+        displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+        val guidelineBounds by collectLastValue(viewModel.guidelineBounds)
+        if (testCase.modalities.hasUdfps) {
+            assertThat(guidelineBounds).isEqualTo(Rect(0, 0, 0, 0))
+        }
+    }
+
+    @Test
     fun iconViewLoaded() = runGenericTest {
         val isIconViewLoaded by collectLastValue(viewModel.isIconViewLoaded)
         // TODO(b/328677869): Add test for noIcon logic.
@@ -1395,9 +1610,10 @@
     private fun runGenericTest(
         doNotStart: Boolean = false,
         allowCredentialFallback: Boolean = false,
+        subtitle: String? = "s",
         description: String? = null,
         contentView: PromptContentView? = null,
-        logoRes: Int = -1,
+        logoRes: Int = 0,
         logoBitmap: Bitmap? = null,
         logoDescription: String? = null,
         packageName: String = OP_PACKAGE_NAME,
@@ -1433,10 +1649,11 @@
             allowCredentialFallback = allowCredentialFallback,
             fingerprint = testCase.fingerprint,
             face = testCase.face,
+            subtitleFromApp = subtitle,
             descriptionFromApp = description,
             contentViewFromApp = contentView,
             logoResFromApp = logoRes,
-            logoBitmapFromApp = logoBitmap,
+            logoBitmapFromApp = if (logoRes != 0) logoDrawableFromAppRes.toBitmap() else logoBitmap,
             logoDescriptionFromApp = logoDescription,
             packageName = packageName,
         )
@@ -1476,7 +1693,7 @@
 
     companion object {
         @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
+        @Parameters(name = "{0}")
         fun data(): Collection<TestCase> = singleModalityTestCases + coexTestCases
 
         private val singleModalityTestCases =
@@ -1620,26 +1837,28 @@
     face: FaceSensorPropertiesInternal? = null,
     requireConfirmation: Boolean = false,
     allowCredentialFallback: Boolean = false,
+    subtitleFromApp: String? = "s",
     descriptionFromApp: String? = null,
     contentViewFromApp: PromptContentView? = null,
-    logoResFromApp: Int = -1,
+    logoResFromApp: Int = 0,
     logoBitmapFromApp: Bitmap? = null,
     logoDescriptionFromApp: String? = null,
     packageName: String = OP_PACKAGE_NAME,
 ) {
     val info =
         PromptInfo().apply {
-            logoRes = logoResFromApp
-            logoBitmap = logoBitmapFromApp
             logoDescription = logoDescriptionFromApp
             title = "t"
-            subtitle = "s"
+            subtitle = subtitleFromApp
             description = descriptionFromApp
             contentView = contentViewFromApp
             authenticators = listOf(face, fingerprint).extractAuthenticatorTypes()
             isDeviceCredentialAllowed = allowCredentialFallback
             isConfirmationRequested = requireConfirmation
         }
+    if (logoBitmapFromApp != null) {
+        info.setLogo(logoResFromApp, logoBitmapFromApp)
+    }
 
     setPrompt(
         info,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
index b065393..3b2cf61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -27,6 +27,7 @@
 import android.view.WindowInsets
 import android.view.WindowMetrics
 import android.view.windowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.airbnb.lottie.model.KeyPath
 import com.android.keyguard.keyguardUpdateMonitor
@@ -62,7 +63,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
@@ -71,7 +71,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SideFpsOverlayViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
index 49f2043..7d4ee25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
@@ -28,11 +28,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.widget.Button;
 import android.widget.TextView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.testing.UiEventLoggerFake;
@@ -58,7 +58,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BroadcastDialogDelegateTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
index 8a1a082..4d7c499 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
@@ -44,7 +44,7 @@
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class AudioSharingInteractorTest : SysuiTestCase() {
     private val testDispatcher = UnconfinedTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
index 4949716..ac5ceb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.bluetooth.qsdialog
 
 import android.bluetooth.BluetoothAdapter
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.systemui.SysuiTestCase
@@ -39,7 +39,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class BluetoothAutoOnInteractorTest : SysuiTestCase() {
     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
     private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt
index 85e2a8d..b7b2be4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.bluetooth.qsdialog
 
 import android.bluetooth.BluetoothAdapter
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.bluetooth.BluetoothEventManager
 import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -38,7 +38,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class BluetoothAutoOnRepositoryTest : SysuiTestCase() {
     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
     private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
index 6fe7d86..993cac7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.bluetooth.qsdialog
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter
 import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -38,7 +38,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class BluetoothStateInteractorTest : SysuiTestCase() {
     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index 7215619..d01fac3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.bluetooth.qsdialog
 
 import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
 import android.view.View
@@ -27,6 +26,7 @@
 import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -57,7 +57,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class BluetoothTileDialogDelegateTest : SysuiTestCase() {
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt
index 4aa6209..1f3dcac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepositoryTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.bluetooth.qsdialog
 
 import android.bluetooth.BluetoothAdapter
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
@@ -35,7 +35,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class BluetoothTileDialogRepositoryTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index 11f74c0..9abb85d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -18,11 +18,11 @@
 
 import android.bluetooth.BluetoothAdapter
 import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.view.View.GONE
 import android.view.View.VISIBLE
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -62,7 +62,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @EnableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
 class BluetoothTileDialogViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt
deleted file mode 100644
index 762137b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt
+++ /dev/null
@@ -1,121 +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.bluetooth.qsdialog
-
-import android.bluetooth.BluetoothDevice
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.whenever
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@OptIn(ExperimentalCoroutinesApi::class)
-class DeviceItemActionInteractorImplTest : SysuiTestCase() {
-    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
-    private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
-    private lateinit var actionInteractorImpl: DeviceItemActionInteractor
-
-    @Mock private lateinit var dialog: SystemUIDialog
-    @Mock private lateinit var cachedDevice: CachedBluetoothDevice
-    @Mock private lateinit var device: BluetoothDevice
-    @Mock private lateinit var deviceItem: DeviceItem
-
-    @Before
-    fun setUp() {
-        actionInteractorImpl = kosmos.deviceItemActionInteractor
-        whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedDevice)
-        whenever(cachedDevice.address).thenReturn("ADDRESS")
-        whenever(cachedDevice.device).thenReturn(device)
-    }
-
-    @Test
-    fun testOnClick_connectedMedia_setActive() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(deviceItem.type)
-                    .thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
-                actionInteractorImpl.onClick(deviceItem, dialog)
-                verify(cachedDevice).setActive()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedDevice.address,
-                        DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_activeMedia_disconnect() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(deviceItem.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE)
-                actionInteractorImpl.onClick(deviceItem, dialog)
-                verify(cachedDevice).disconnect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(
-                        cachedDevice.address,
-                        DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE
-                    )
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_connectedOtherDevice_disconnect() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(deviceItem.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
-                actionInteractorImpl.onClick(deviceItem, dialog)
-                verify(cachedDevice).disconnect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(cachedDevice.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
-            }
-        }
-    }
-
-    @Test
-    fun testOnClick_saved_connect() {
-        with(kosmos) {
-            testScope.runTest {
-                whenever(deviceItem.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
-                actionInteractorImpl.onClick(deviceItem, dialog)
-                verify(cachedDevice).connect()
-                verify(bluetoothTileDialogLogger)
-                    .logDeviceClick(cachedDevice.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
index e8e37bc..5ff4634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
@@ -13,19 +13,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.systemui.bluetooth.qsdialog
 
 import com.android.internal.logging.uiEventLogger
+import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.plugins.activityStarter
 import com.android.systemui.util.mockito.mock
 
 val Kosmos.bluetoothTileDialogLogger: BluetoothTileDialogLogger by Kosmos.Fixture { mock {} }
 
+val Kosmos.localBluetoothManager: LocalBluetoothManager by Kosmos.Fixture { mock {} }
+
+val Kosmos.dialogTransitionAnimator: DialogTransitionAnimator by Kosmos.Fixture { mock {} }
+
 val Kosmos.deviceItemActionInteractor: DeviceItemActionInteractor by
     Kosmos.Fixture {
-        DeviceItemActionInteractorImpl(
+        DeviceItemActionInteractor(
+            activityStarter,
+            dialogTransitionAnimator,
+            localBluetoothManager,
             testDispatcher,
             bluetoothTileDialogLogger,
             uiEventLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
new file mode 100644
index 0000000..8246506
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt
@@ -0,0 +1,459 @@
+/*
+ * 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.bluetooth.qsdialog
+
+import android.bluetooth.BluetoothDevice
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.settingslib.bluetooth.BluetoothUtils
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.bluetooth.LeAudioProfile
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@OptIn(ExperimentalCoroutinesApi::class)
+class DeviceItemActionInteractorTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+    private lateinit var actionInteractorImpl: DeviceItemActionInteractor
+    private lateinit var mockitoSession: StaticMockitoSession
+    private lateinit var activeMediaDeviceItem: DeviceItem
+    private lateinit var notConnectedDeviceItem: DeviceItem
+    private lateinit var connectedMediaDeviceItem: DeviceItem
+    private lateinit var connectedOtherDeviceItem: DeviceItem
+    @Mock private lateinit var dialog: SystemUIDialog
+    @Mock private lateinit var profileManager: LocalBluetoothProfileManager
+    @Mock private lateinit var leAudioProfile: LeAudioProfile
+    @Mock private lateinit var assistantProfile: LocalBluetoothLeBroadcastAssistant
+    @Mock private lateinit var bluetoothDevice: BluetoothDevice
+    @Mock private lateinit var bluetoothDeviceGroupId2: BluetoothDevice
+    @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice
+
+    @Before
+    fun setUp() {
+        mockitoSession =
+            mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
+        activeMediaDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
+        notConnectedDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.SAVED_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
+        connectedMediaDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
+        connectedOtherDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
+        actionInteractorImpl = kosmos.deviceItemActionInteractor
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+    }
+
+    @Test
+    fun testOnClick_connectedMedia_setActive() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(cachedBluetoothDevice).setActive()
+                verify(bluetoothTileDialogLogger)
+                    .logDeviceClick(
+                        cachedBluetoothDevice.address,
+                        DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_activeMedia_disconnect() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+                actionInteractorImpl.onClick(activeMediaDeviceItem, dialog)
+                verify(cachedBluetoothDevice).disconnect()
+                verify(bluetoothTileDialogLogger)
+                    .logDeviceClick(
+                        cachedBluetoothDevice.address,
+                        DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_connectedOtherDevice_disconnect() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+                actionInteractorImpl.onClick(connectedOtherDeviceItem, dialog)
+                verify(cachedBluetoothDevice).disconnect()
+                verify(bluetoothTileDialogLogger)
+                    .logDeviceClick(
+                        cachedBluetoothDevice.address,
+                        DeviceItemType.CONNECTED_BLUETOOTH_DEVICE
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_saved_connect() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(cachedBluetoothDevice).connect()
+                verify(bluetoothTileDialogLogger)
+                    .logDeviceClick(
+                        cachedBluetoothDevice.address,
+                        DeviceItemType.SAVED_BLUETOOTH_DEVICE
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_audioSharingDisabled_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(cachedBluetoothDevice.connectableProfiles)
+                        .thenReturn(listOf(leAudioProfile))
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
+                whenever(
+                        BluetoothUtils.hasConnectedBroadcastSource(
+                            ArgumentMatchers.any(),
+                            ArgumentMatchers.any()
+                        )
+                    )
+                    .thenReturn(true)
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+                whenever(cachedBluetoothDevice.connectableProfiles)
+                        .thenReturn(listOf(leAudioProfile))
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true)
+                whenever(
+                    BluetoothUtils.hasConnectedBroadcastSource(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.any()
+                    )
+                )
+                        .thenReturn(false)
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter)
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_noConnectedLeDevice_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasOneConnectedLeDevice_clickedNonLe_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice))
+
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasOneConnectedLeDevice_clickedLe_shouldLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile))
+                whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice))
+
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(activityStarter)
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasOneConnectedLeDevice_clickedConnectedLe_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice))
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasTwoConnectedLeDevice_clickedNotConnectedLe_shouldNotLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2))
+                whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer {
+                    val device = it.arguments.first() as BluetoothDevice
+                    if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
+                }
+
+                actionInteractorImpl.onClick(notConnectedDeviceItem, dialog)
+                verify(activityStarter, Mockito.never())
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    @Test
+    fun testOnClick_hasTwoConnectedLeDevice_clickedConnectedLe_shouldLaunchSettings() {
+        with(kosmos) {
+            testScope.runTest {
+                whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
+                whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS)
+                whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile))
+                whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true)
+
+                whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true)
+                whenever(localBluetoothManager.profileManager).thenReturn(profileManager)
+                whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile)
+                whenever(profileManager.leAudioBroadcastAssistantProfile)
+                    .thenReturn(assistantProfile)
+
+                whenever(
+                        assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any())
+                    )
+                    .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2))
+                whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer {
+                    val device = it.arguments.first() as BluetoothDevice
+                    if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
+                }
+
+                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                verify(activityStarter)
+                    .postStartActivityDismissingKeyguard(
+                        ArgumentMatchers.any(),
+                        ArgumentMatchers.anyInt(),
+                        ArgumentMatchers.any()
+                    )
+            }
+        }
+    }
+
+    private companion object {
+        const val DEVICE_NAME = "device"
+        const val DEVICE_CONNECTION_SUMMARY = "active"
+        const val DEVICE_ADDRESS = "address"
+        const val GROUP_ID_1 = 1
+        const val GROUP_ID_2 = 2
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
index 4bcd9a9..a27ccc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactoryTest.kt
@@ -22,8 +22,8 @@
 import android.media.AudioManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.flags.Flags
@@ -39,7 +39,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class DeviceItemFactoryTest : SysuiTestCase() {
     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
index 2b4f950..7f7abaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractorTest.kt
@@ -20,8 +20,8 @@
 import android.bluetooth.BluetoothDevice
 import android.content.Context
 import android.media.AudioManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
@@ -43,7 +43,7 @@
 import org.mockito.junit.MockitoRule
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class DeviceItemInteractorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
index ca95822..923687b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
@@ -25,11 +25,13 @@
 import com.google.common.truth.Truth.assertThat
 import java.util.Locale
 import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameter
+import platform.test.runner.parameterized.Parameters
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 class BouncerSceneLayoutTest : SysuiTestCase() {
 
     data object Phone :
@@ -79,7 +81,7 @@
 
     companion object {
         @JvmStatic
-        @Parameterized.Parameters(name = "{0}")
+        @Parameters(name = "{0}")
         fun testCases() =
             listOf(
                     Phone to
@@ -158,7 +160,7 @@
                 }
     }
 
-    @Parameterized.Parameter @JvmField var testCase: TestCase? = null
+    @Parameter @JvmField var testCase: TestCase? = null
 
     @Test
     fun calculateLayout() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
index 8e81727..1e9f855 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
@@ -20,7 +20,7 @@
 import android.content.Intent
 import android.os.Bundle
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -34,7 +34,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class BroadcastSenderTest : SysuiTestCase() {
 
@@ -138,4 +138,4 @@
         verification.invoke()
         assertThat(wakeLock.isHeld).isFalse()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
index 43d2cb8..c693ecc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
@@ -2,7 +2,7 @@
 
 import android.content.BroadcastReceiver
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
@@ -14,7 +14,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class PendingRemovalStoreTest : SysuiTestCase() {
 
@@ -78,4 +78,4 @@
         assertThat(store.isPendingRemoval(receiverOne, user)).isTrue()
         assertThat(store.isPendingRemoval(receiverTwo, user)).isFalse()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
index 582f301..d878352 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -21,8 +21,8 @@
 import android.content.IntentFilter
 import android.os.Handler
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
@@ -43,7 +43,7 @@
 import org.mockito.MockitoAnnotations
 import java.util.concurrent.Executor
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @SmallTest
 class UserBroadcastDispatcherTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 669795b..bea0db6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -24,6 +24,7 @@
 import android.content.pm.ActivityInfo
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.ActivityIntentHelper
 import com.android.systemui.SysuiTestCase
@@ -41,7 +42,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
@@ -50,7 +50,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class CameraGestureHelperTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
index 1e522fc..3494024 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.camera
 
 import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
 import com.android.systemui.SysuiTestCase
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -26,7 +26,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class CameraIntentsTest : SysuiTestCase() {
     companion object {
         val VALID_SECURE_INTENT = Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 11756d5..034bab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -17,11 +17,11 @@
 package com.android.systemui.charging
 
 import android.graphics.Rect
-import android.testing.AndroidTestingRunner
 import android.view.Surface
 import android.view.View
 import android.view.WindowManager
 import android.view.WindowMetrics
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.res.R
@@ -49,7 +49,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class WiredChargingRippleControllerTest : SysuiTestCase() {
     private lateinit var controller: WiredChargingRippleController
     @Mock private lateinit var commandRegistry: CommandRegistry
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 6afbde0..88bfcf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -29,10 +29,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -58,7 +58,7 @@
 import java.util.Set;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class BrightLineClassifierTest extends SysuiTestCase {
     private BrightLineFalsingManager mBrightLineFalsingManager;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index 6e00b70..ec8cc4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -25,10 +25,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -53,7 +53,7 @@
 import java.util.Set;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class BrightLineFalsingManagerTest extends SysuiTestCase {
     private BrightLineFalsingManager mBrightLineFalsingManager;
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
index 14dcd58..8e1be41 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
@@ -24,8 +24,8 @@
 
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.util.DeviceConfigProxyFake;
@@ -38,7 +38,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class DiagonalClassifierTest extends ClassifierTest {
 
     // Next variable is not actually five, but is very close. 5 degrees is currently the value
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
index ab6d5b7..cbfecee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
@@ -21,8 +21,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.util.DeviceConfigProxyFake;
@@ -33,7 +33,7 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class DistanceClassifierTest extends ClassifierTest {
 
     private FalsingDataProvider mDataProvider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
index 2ceee6d..9289867 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
@@ -22,9 +22,9 @@
 import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.view.MotionEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
@@ -38,7 +38,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class DoubleTapClassifierTest extends ClassifierTest {
 
     private static final int TOUCH_SLOP = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
index 2c904e7..8e4bec3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
@@ -16,10 +16,10 @@
 
 package com.android.systemui.classifier
 
-import android.testing.AndroidTestingRunner
 import android.view.View
 import android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK
 import android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Before
@@ -31,7 +31,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class FalsingA11yDelegateTest : SysuiTestCase() {
     @Mock lateinit var falsingCollector: FalsingCollector
     @Mock lateinit var view: View
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 5361cef..5d0bfd7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -25,11 +25,11 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -66,7 +66,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class FalsingCollectorImplTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index 057b0a1..49c6239 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -25,11 +25,11 @@
 import static org.mockito.Mockito.when;
 
 import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
-import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener;
@@ -46,7 +46,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class FalsingDataProviderTest extends ClassifierTest {
 
     private FalsingDataProvider mDataProvider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
index 38355c7..8e19a1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
@@ -18,8 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -33,7 +33,7 @@
 import java.util.Collections;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class HistoryTrackerTest extends SysuiTestCase {
 
     private FakeSystemClock mSystemClock = new FakeSystemClock();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java
index b8ea062..352a25c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java
@@ -23,9 +23,9 @@
 import static org.mockito.ArgumentMatchers.anyDouble;
 import static org.mockito.ArgumentMatchers.anyInt;
 
-import android.testing.AndroidTestingRunner;
 import android.view.MotionEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
@@ -34,7 +34,7 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class PointerCountClassifierTest extends ClassifierTest {
 
     private FalsingClassifier mClassifier;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
index 1c3922a..f965a11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
@@ -24,9 +24,9 @@
 
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.view.MotionEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.plugins.FalsingManager;
@@ -40,7 +40,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ProximityClassifierTest extends ClassifierTest {
 
     private static final long NS_PER_MS = 1000000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
index e3c800e..65e9088 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
@@ -20,9 +20,9 @@
 
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.view.MotionEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
@@ -36,7 +36,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class SingleTapClassifierTest extends ClassifierTest {
 
     private static final int TOUCH_SLOP = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
index ad7afa3..9a27f38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
@@ -19,11 +19,11 @@
 import static org.hamcrest.CoreMatchers.is;
 import static org.junit.Assert.assertThat;
 
-import android.testing.AndroidTestingRunner;
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class TimeLimitedInputEventBufferTest extends SysuiTestCase {
 
     private static final long MAX_AGE_MS = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
index 588edb7..80c44e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
@@ -33,8 +33,8 @@
 
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -44,7 +44,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class TypeClassifierTest extends ClassifierTest {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
index ae2b8bb..1fe7268 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java
@@ -20,8 +20,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.util.DeviceConfigProxyFake;
@@ -34,7 +34,7 @@
 import java.util.Random;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ZigZagClassifierTest extends ClassifierTest {
 
     private FalsingClassifier mClassifier;
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 c0dada4..5d76e32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardModelTest.kt
@@ -22,8 +22,8 @@
 import android.graphics.Bitmap
 import android.net.Uri
 import android.os.PersistableBundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.whenever
 import java.io.IOException
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
index d552c9d..de07cda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
@@ -14,7 +14,7 @@
 
 package com.android.systemui.common.coroutine
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
 /** atest SystemUITests:CoroutineResultTest */
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class CoroutineResultTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
index 2f4fc96..bb400f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.common.ui.view
 
 import android.view.ViewConfiguration
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.view.LongPressHandlingViewInteractionHandler.MotionEventModel
@@ -33,7 +34,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
@@ -41,7 +41,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class LongPressHandlingViewInteractionHandlerTest : SysuiTestCase() {
 
     @Mock private lateinit var postDelayed: (Runnable, Long) -> DisposableHandle
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
index 4c4205e..cecb525 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
@@ -25,12 +25,12 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.SeekBar;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -49,7 +49,7 @@
  * Tests for {@link SeekBarWithIconButtonsView}
  */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class SeekBarWithIconButtonsViewTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
index 288f3b6..ed21474 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
@@ -21,10 +21,10 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import androidx.lifecycle.Observer;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -48,7 +48,7 @@
 import java.util.HashSet;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class ComplicationCollectionLiveDataTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
index c43df17..dd3f991 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
@@ -24,13 +24,13 @@
 
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.view.View;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.Observer;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -51,7 +51,7 @@
 import java.util.HashSet;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ComplicationHostViewControllerTest extends SysuiTestCase {
     @Mock
     ConstraintLayout mComplicationHostView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
index baaeee1..383e0fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
@@ -22,10 +22,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.view.View;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -48,7 +48,7 @@
 import java.util.stream.Collectors;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ComplicationLayoutEngineTest extends SysuiTestCase {
     @Mock
     ConstraintLayout mLayout;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
index a23e9e4..12cb8a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
@@ -21,8 +21,8 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -36,7 +36,7 @@
 import java.util.function.Consumer;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ComplicationLayoutParamsTest extends SysuiTestCase {
     /**
      * Ensures ComplicationLayoutParams cannot be constructed with improper position or direction.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
index 8cd23b2..d728517 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
@@ -25,8 +25,8 @@
 import android.database.ContentObserver;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.dream.DreamBackend;
@@ -50,7 +50,7 @@
 import java.util.HashSet;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ComplicationTypesUpdaterTest extends SysuiTestCase {
     @Mock
     private Context mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java
index e23e1f4..1e80233 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java
@@ -29,8 +29,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.dream.DreamBackend;
@@ -45,7 +45,7 @@
 import java.util.Set;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ComplicationUtilsTest extends SysuiTestCase {
     @Test
     public void testConvertComplicationType() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
index 09675e2..98b119a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
@@ -21,9 +21,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 
 import androidx.lifecycle.ViewModel;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -38,7 +38,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ComplicationViewModelTransformerTest extends SysuiTestCase {
     @Mock
     ComplicationViewModelComponent.Factory mFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
index b9aa4c6..22ab499 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
@@ -23,9 +23,9 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.testing.AndroidTestingRunner;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
@@ -42,7 +42,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class DreamClockTimeComplicationTest extends SysuiTestCase {
     @SuppressWarnings("HidingField")
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 18bd960b..ddf69b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -29,9 +29,9 @@
 
 import android.content.ComponentName;
 import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
@@ -62,7 +62,7 @@
 import java.util.Optional;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class DreamHomeControlsComplicationTest extends SysuiTestCase {
     @Mock
     private DreamHomeControlsComplication mComplication;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
index 05b4a41..3a856a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
@@ -26,10 +26,10 @@
 
 import android.app.PendingIntent;
 import android.content.Intent;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.ActivityIntentHelper;
@@ -51,7 +51,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class DreamMediaEntryComplicationTest extends SysuiTestCase {
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
index 87de865..6c354ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
@@ -24,9 +24,9 @@
 import static org.mockito.Mockito.when;
 
 import android.app.smartspace.SmartspaceTarget;
-import android.testing.AndroidTestingRunner;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -52,7 +52,7 @@
 import java.util.Set;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class SmartSpaceComplicationTest extends SysuiTestCase {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
index 03e4f9a..c2fe009 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/compose/ComposeInitializerTest.kt
@@ -17,10 +17,10 @@
 package com.android.systemui.compose
 
 import android.content.Context
-import android.testing.AndroidTestingRunner
 import android.testing.ViewUtils
 import android.widget.FrameLayout
 import androidx.compose.ui.platform.ComposeView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ComposeInitializerTest : SysuiTestCase() {
     @Test
     fun testCanAddComposeViewInInitializedWindow() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
index 4d0f2ed..28e0cff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName
 import android.graphics.drawable.Icon
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Assert.assertNull
@@ -30,7 +30,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class CustomIconCacheTest : SysuiTestCase() {
 
     companion object {
@@ -98,4 +98,4 @@
 
         assertNull(customIconCache.retrieve(TEST_COMPONENT1, CONTROL_ID_1))
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt
index 129fe9a..8d6e3a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.controls.controller
 
 import android.content.ComponentName
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Assert.assertEquals
@@ -37,7 +37,7 @@
 import java.io.File
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class AuxiliaryPersistenceWrapperTest : SysuiTestCase() {
 
     companion object {
@@ -128,4 +128,4 @@
 
         verify(persistenceWrapper, never()).storeFavorites(ArgumentMatchers.anyList())
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 6cc3ef19..9285146 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.controls.ui
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
 import android.view.HapticFeedbackConstants
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastSender
@@ -48,7 +48,7 @@
 import java.util.Optional
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlActionCoordinatorImplTest : SysuiTestCase() {
     @Mock
     private lateinit var vibratorHelper: VibratorHelper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index 724c9d1..ed0c7ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -24,7 +24,7 @@
 import android.service.controls.DeviceTypes
 import android.service.controls.IControlsSubscriber
 import android.service.controls.IControlsSubscription
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserTracker
@@ -49,7 +49,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsBindingControllerImplTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index de455f63..cf385e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -26,7 +26,7 @@
 import android.service.controls.Control
 import android.service.controls.DeviceTypes
 import android.service.controls.actions.ControlAction
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.backup.BackupHelper
@@ -74,7 +74,7 @@
 import java.util.function.Consumer
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsControllerImplTest : SysuiTestCase() {
     private val kosmos = testKosmos()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
index 690b9a7..afa5cec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName
 import android.service.controls.DeviceTypes
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -32,7 +32,7 @@
 import java.io.File
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsFavoritePersistenceWrapperTest : SysuiTestCase() {
 
     private lateinit var file: File
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
index b5d3476..f9c2c6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
@@ -26,7 +26,7 @@
 import android.service.controls.IControlsSubscriber
 import android.service.controls.actions.ControlAction
 import android.service.controls.actions.ControlActionWrapper
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -57,7 +57,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsProviderLifecycleManagerTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
index 581e88b..e04ce45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.controls.controller
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -25,7 +25,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class ControlsTileResourceConfigurationImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
index 2283746..b6ea62e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt
@@ -19,7 +19,7 @@
 import android.app.job.JobParameters
 import android.content.Context
 import android.os.PersistableBundle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper.DeletionJobService.Companion.USER
@@ -37,7 +37,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class DeletionJobServiceTest : SysuiTestCase() {
 
     @Mock private lateinit var context: Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
index 85d6211..282ea5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
@@ -20,7 +20,7 @@
 import android.content.pm.PackageManager
 import android.os.Handler
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.any
@@ -39,7 +39,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class PackageUpdateMonitorTest : SysuiTestCase() {
 
     @Mock private lateinit var context: Context
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
index 789d6df..b5c6c53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
@@ -23,7 +23,7 @@
 import android.service.controls.IControlsSubscription
 import android.service.controls.actions.ControlAction
 import android.service.controls.actions.ControlActionWrapper
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Assert.assertEquals
@@ -42,7 +42,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ServiceWrapperTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
index 267520e..7d197f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
@@ -20,7 +20,7 @@
 import android.os.Binder
 import android.service.controls.Control
 import android.service.controls.IControlsSubscription
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -36,7 +36,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class StatefulControlSubscriberTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
index 54f66dc..844cc1f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.controls.dagger
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
@@ -45,7 +45,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsComponentTest : SysuiTestCase() {
 
     @Mock private lateinit var controller: ControlsController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
index 4ea9616..5528f65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -19,7 +19,7 @@
 import android.app.PendingIntent
 import android.content.ComponentName
 import android.service.controls.Control
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlStatus
@@ -37,7 +37,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class AllModelTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
index 226ef3b..56c7c85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
@@ -18,10 +18,10 @@
 
 import android.content.ComponentName
 import android.content.res.Resources
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.core.lifecycle.Lifecycle
 import com.android.systemui.SysuiTestCase
@@ -45,7 +45,7 @@
 import java.text.Collator
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class AppAdapterTest : SysuiTestCase() {
     private val fakeSystemClock = FakeSystemClock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
index 2a4524b..39e1e1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -3,12 +3,12 @@
 import android.content.ComponentName
 import android.content.Intent
 import android.os.Bundle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.Button
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import com.android.systemui.res.R
@@ -33,7 +33,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ControlsEditingActivityTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index 88d36af..f5616d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -4,12 +4,12 @@
 import android.content.Intent
 import android.os.Bundle
 import android.service.controls.Control
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.Button
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
@@ -45,7 +45,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ControlsFavoritingActivityTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index 6361e94..e4f0910 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -28,7 +28,7 @@
 import android.os.Bundle
 import android.os.UserHandle
 import android.service.controls.ControlsProviderService
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.applications.ServiceListing
 import com.android.systemui.res.R
@@ -65,7 +65,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsListingControllerImplTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
index d17495f..7698520 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsProviderSelectorActivityTest.kt
@@ -23,10 +23,10 @@
 import android.content.pm.ServiceInfo
 import android.graphics.drawable.Drawable
 import android.os.Bundle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import com.android.systemui.SysuiTestCase
@@ -65,7 +65,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ControlsProviderSelectorActivityTest : SysuiTestCase() {
     @Main private val executor: Executor = MoreExecutors.directExecutor()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
index ca970bb..5008927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
@@ -25,9 +25,9 @@
 import android.service.controls.Control
 import android.service.controls.ControlsProviderService
 import android.service.controls.DeviceTypes
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.lifecycle.Lifecycle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.rule.ActivityTestRule
 import com.android.systemui.SysuiTestCase
@@ -53,7 +53,7 @@
 import java.util.concurrent.Executor
 
 @MediumTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ControlsRequestDialogTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
index ae77d1f..c49867a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
@@ -30,7 +30,7 @@
 import android.os.UserHandle
 import android.service.controls.Control
 import android.service.controls.ControlsProviderService
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Assert.assertEquals
@@ -49,7 +49,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsRequestReceiverTest : SysuiTestCase() {
 
     @Mock
@@ -266,4 +266,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
index f0003ed..281addc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.controls.management
 
 import android.content.ComponentName
-import android.testing.AndroidTestingRunner
 import androidx.recyclerview.widget.RecyclerView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlInterface
@@ -43,7 +43,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class FavoritesModelTest : SysuiTestCase() {
 
     companion object {
@@ -299,4 +299,4 @@
     }
 
     private fun getDividerPosition(): Int = model.elements.indexOf(dividerWrapper)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
index 7f0ea9a..d8aac10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
@@ -19,7 +19,7 @@
 
 import android.content.Context
 import android.content.DialogInterface
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.res.R
@@ -39,7 +39,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class PanelConfirmationDialogFactoryTest : SysuiTestCase() {
 
     @Mock private lateinit var mockDialog : SystemUIDialog
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
index 18ce4a8..fd4c681 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
@@ -19,7 +19,7 @@
 
 import android.content.SharedPreferences
 import android.content.pm.UserInfo
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -37,7 +37,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() {
     val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
index a7e7ba9..86e3481 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
@@ -19,7 +19,7 @@
 import android.content.ComponentName
 import android.content.SharedPreferences
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -43,7 +43,7 @@
 import org.mockito.MockitoAnnotations
 
 @ExperimentalCoroutinesApi
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class SelectedComponentRepositoryTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
index 154c373..aee334f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
@@ -22,8 +22,8 @@
 import android.database.ContentObserver
 import android.provider.Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS
 import android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS
-import android.testing.AndroidTestingRunner
 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.controls.settings.ControlsSettingsDialogManager.Companion.PREFS_SETTINGS_DIALOG_ATTEMPTS
@@ -52,7 +52,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ControlsSettingsDialogManagerImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
index b904ac1..3bdd5cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
@@ -19,6 +19,7 @@
 
 import android.content.pm.UserInfo
 import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.user.data.repository.FakeUserRepository
@@ -33,10 +34,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @OptIn(ExperimentalCoroutinesApi::class)
 class ControlsSettingsRepositoryImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
index c44429b..9e8914a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/start/ControlsStartableTest.kt
@@ -26,7 +26,7 @@
 import android.content.pm.ServiceInfo
 import android.os.UserHandle
 import android.os.UserManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -75,7 +75,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsStartableTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
index bfdb923..193ce21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
@@ -21,7 +21,7 @@
 import android.graphics.drawable.Icon
 import android.net.Uri
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -29,7 +29,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class CanUseIconPredicateTest : SysuiTestCase() {
 
     private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
index 101b8ed..4b30fa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
@@ -24,11 +24,11 @@
 import android.service.controls.Control
 import android.service.controls.DeviceTypes
 import android.service.controls.templates.ControlTemplate
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -44,7 +44,7 @@
 import org.mockito.Mockito.mock
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ControlViewHolderTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt
index e279d28..03aa622 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsActivityTest.kt
@@ -19,8 +19,8 @@
 import android.content.Intent
 import android.content.res.Configuration
 import android.service.dreams.IDreamManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import com.android.systemui.SysuiTestCase
@@ -39,7 +39,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ControlsActivityTest : SysuiTestCase() {
     @Mock private lateinit var uiController: ControlsUiController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
index 38c6a0e..ca33f16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
@@ -18,7 +18,7 @@
 package com.android.systemui.controls.ui
 
 import android.content.Context
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -36,7 +36,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ControlsDialogsFactoryTest : SysuiTestCase() {
 
     private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
index 48e3962..66303eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
@@ -19,12 +19,12 @@
 import android.app.Activity
 import android.graphics.Color
 import android.graphics.drawable.ShapeDrawable
-import android.testing.AndroidTestingRunner
 import android.util.DisplayMetrics
 import android.view.View
 import android.view.ViewGroup
 import android.widget.PopupWindow.OnDismissListener
 import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -43,7 +43,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 open class ControlsPopupMenuTest : SysuiTestCase() {
 
     private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
index 8f3813d..20890a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt
@@ -26,13 +26,13 @@
 import android.os.UserHandle
 import android.service.controls.ControlsProviderService
 import android.service.controls.flags.Flags.FLAG_HOME_PANEL_DREAM
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.ControlsMetricsLogger
@@ -83,7 +83,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ControlsUiControllerImplTest : SysuiTestCase() {
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
index 677108c..10b3ce3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
@@ -19,9 +19,9 @@
 import android.app.ActivityOptions
 import android.app.PendingIntent
 import android.content.Context
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.activity.EmptyTestActivity
@@ -44,7 +44,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class DetailDialogTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
index 483ab3b..6092b8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
@@ -17,7 +17,7 @@
 
 package com.android.systemui.controls.ui
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -25,7 +25,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class OverflowMenuAdapterTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
index 021facc..de2d852 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
@@ -25,8 +25,8 @@
 import android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK
 import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 import android.graphics.Rect
-import android.testing.AndroidTestingRunner
 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.util.boundsOnScreen
@@ -49,7 +49,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class PanelTaskViewControllerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt
index 57176f0..4579807 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/SelectionItemTest.kt
@@ -1,7 +1,7 @@
 package com.android.systemui.controls.ui
 
 import android.content.ComponentName
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.controller.StructureInfo
@@ -11,7 +11,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class SelectionItemTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
index 31e0954..9f4836a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.controls.ui
 
 import android.service.controls.templates.RangeTemplate
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Assert.assertEquals
@@ -25,7 +25,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ToggleRangeTemplateTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
index 1e4753e..23da3f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
@@ -1,6 +1,6 @@
 package com.android.systemui.coroutines
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -12,7 +12,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class FlowTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
index 1040ec4..f029847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.Insets
 import android.graphics.Rect
-import android.testing.AndroidTestingRunner
 import android.testing.TestableResources
 import android.util.RotationUtils
 import android.util.Size
@@ -27,6 +26,7 @@
 import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH
 import android.view.DisplayInfo
 import android.view.Surface
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.any
@@ -38,7 +38,7 @@
 import org.mockito.Mockito.doAnswer
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class CutoutDecorProviderFactoryTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
index a1cffc1..69fab56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
@@ -17,11 +17,11 @@
 package com.android.systemui.decor
 
 import android.graphics.Color
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.DisplayCutout
 import android.view.Surface
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -34,7 +34,7 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class OverlayWindowTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
index e4ddc37..6d6c6ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.decor
 
 import android.content.res.Resources
-import android.testing.AndroidTestingRunner
 import android.view.DisplayCutout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -30,7 +30,7 @@
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.`when` as whenever
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() {
     private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory
@@ -83,4 +83,4 @@
                     and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT))
         })
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
index d1d4880..4da988a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.decor
 
-import android.testing.AndroidTestingRunner
 import android.util.Size
 import android.view.DisplayCutout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -30,7 +30,7 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.spy
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() {
 
@@ -139,4 +139,4 @@
             })
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
index 2bff7d2..9d440c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
@@ -18,9 +18,9 @@
 
 import android.content.res.TypedArray
 import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
 import android.util.Size
 import androidx.annotation.DrawableRes
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R as InternalR
 import com.android.systemui.res.R as SystemUIR
@@ -33,7 +33,7 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class RoundedCornerResDelegateTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
index 6c2e136..4793a52f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
@@ -18,8 +18,8 @@
 
 import android.content.Intent
 import android.os.Bundle
-import android.testing.AndroidTestingRunner
 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.demomode.DemoMode.ACTION_DEMO
@@ -40,7 +40,7 @@
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @SmallTest
 class DemoModeControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
index e9b4bbb..6b0de92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/data/repository/FaceWakeUpTriggersConfigTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.deviceentry.data.repository
 
 import android.os.PowerManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
@@ -30,7 +30,7 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class FaceWakeUpTriggersConfigTest : SysuiTestCase() {
     @Mock lateinit var globalSettings: GlobalSettings
diff --git a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt
index 8203291..64ff5f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt
@@ -22,6 +22,7 @@
 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA
 import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserTracker
@@ -31,13 +32,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DevicePolicyManagerExtTest : SysuiTestCase() {
 
     @Mock lateinit var devicePolicyManager: DevicePolicyManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
index c79cbab..3f5b9a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.display.data.repository
 
 import android.hardware.devicestate.DeviceStateManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.R
 import com.android.systemui.SysuiTestCase
@@ -41,7 +41,7 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
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 68d49c7..01868ae 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
@@ -18,11 +18,11 @@
 
 import android.hardware.display.DisplayManager
 import android.os.Looper
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.Display
 import android.view.Display.TYPE_EXTERNAL
 import android.view.Display.TYPE_INTERNAL
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.FlowValue
@@ -46,7 +46,7 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
index 37c7409..fd9964f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
@@ -19,12 +19,12 @@
 import android.companion.virtual.VirtualDeviceManager
 import android.companion.virtual.flags.Flags.FLAG_INTERACTIVE_SCREEN_MIRROR
 import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.Display
 import android.view.Display.TYPE_EXTERNAL
 import android.view.Display.TYPE_INTERNAL
 import android.view.Display.TYPE_VIRTUAL
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.FlowValue
@@ -53,7 +53,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mockito.anyInt
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt
index d118cc7..8105bc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogDelegateTest.kt
@@ -18,13 +18,13 @@
 
 import android.app.Dialog
 import android.graphics.Insets
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
 import android.view.View
 import android.view.Window
 import android.view.WindowInsets
 import android.view.WindowInsetsAnimation
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.app.animation.Interpolators
 import com.android.systemui.SysuiTestCase
@@ -42,7 +42,7 @@
 import org.mockito.Mockito.verify
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class MirroringConfirmationDialogDelegateTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 6d2df19..8c125f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -28,9 +28,9 @@
 
 import android.app.ActivityManager;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -46,7 +46,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 public class DozeDockHandlerTest extends SysuiTestCase {
     @Mock private DozeMachine mMachine;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 27fd3b1..aa5edae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -45,8 +45,8 @@
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -71,7 +71,7 @@
 import java.util.Optional;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class DozeScreenBrightnessTest extends SysuiTestCase {
 
     private static final int DEFAULT_BRIGHTNESS = 10;
@@ -583,4 +583,4 @@
     private void waitForSensorManager() {
         mFakeExecutor.runAllReady();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 3cc0451..9c127b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -42,10 +42,10 @@
 import android.database.ContentObserver;
 import android.hardware.Sensor;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -75,7 +75,7 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class DozeSensorsTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
index 92941f9..fad52e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -38,9 +38,9 @@
 
 import android.app.ActivityManager;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
 import android.testing.UiThreadTest;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -60,7 +60,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @UiThreadTest
 public class DozeSuppressorTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 3a6b075..3d1a0d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -21,12 +21,14 @@
 import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -35,14 +37,16 @@
 import android.app.StatusBarManager;
 import android.hardware.Sensor;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.Display;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -71,11 +75,12 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
 public class DozeTriggersTest extends SysuiTestCase {
 
@@ -85,6 +90,7 @@
     private DozeHost mHost;
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
+    private final AmbientDisplayConfiguration mConfig = DozeConfigurationUtil.createMockConfig();
     @Mock
     private DockManager mDockManager;
     @Mock
@@ -105,6 +111,8 @@
     private SelectedUserInteractor mSelectedUserInteractor;
     @Mock
     private SessionTracker mSessionTracker;
+    @Captor
+    private ArgumentCaptor<DozeHost.Callback> mHostCallbackCaptor;
 
     private DozeTriggers mTriggers;
     private FakeSensorManager mSensors;
@@ -116,7 +124,7 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         setupDozeTriggers(
-                DozeConfigurationUtil.createMockConfig(),
+                mConfig,
                 DozeConfigurationUtil.createMockParameters());
     }
 
@@ -174,10 +182,69 @@
     }
 
     @Test
+    public void testOnNotification_startsPulseRequest() {
+        // GIVEN device is dozing
+        Runnable pulseSuppressListener = mock(Runnable.class);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+        doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture());
+        mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
+        clearInvocations(mMachine);
+
+        // WHEN receive an alerting notification
+        mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener);
+
+        // THEN entering to pulse
+        verify(mHost).setPulsePending(true);
+        // AND suppress listeners are NOT notified
+        verify(pulseSuppressListener, never()).run();
+    }
+
+    @Test
+    public void testOnNotification_cannotPulse_notificationSuppressed() {
+        // GIVEN device is dozing
+        Runnable pulseSuppressListener = mock(Runnable.class);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+        doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture());
+        mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
+        clearInvocations(mMachine);
+        // AND pulsing is disabled
+        when(mConfig.pulseOnNotificationEnabled(anyInt())).thenReturn(false);
+
+        // WHEN receive an alerting notification
+        mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener);
+
+        // THEN NOT starting pulse
+        verify(mHost, never()).setPulsePending(anyBoolean());
+        // AND the notification is suppressed
+        verify(pulseSuppressListener).run();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NOTIFICATION_PULSING_FIX)
+    public void testOnNotification_alreadyPulsing_notificationNotSuppressed() {
+        // GIVEN device is pulsing
+        Runnable pulseSuppressListener = mock(Runnable.class);
+        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING);
+        doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture());
+        mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
+        mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE_PULSING);
+        clearInvocations(mMachine);
+
+        // WHEN receive an alerting notification
+        mHostCallbackCaptor.getValue().onNotificationAlerted(pulseSuppressListener);
+
+        // THEN entering to pulse
+        verify(mHost, never()).setPulsePending(anyBoolean());
+        // AND suppress listeners are NOT notified
+        verify(pulseSuppressListener, never()).run();
+    }
+
+    @Test
     public void testOnNotification_noPulseIfPulseIsNotPendingAnymore() {
         when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
-        ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class);
-        doAnswer(invocation -> null).when(mHost).addCallback(captor.capture());
+        doAnswer(invocation -> null).when(mHost).addCallback(mHostCallbackCaptor.capture());
 
         mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED);
         mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
@@ -189,7 +256,7 @@
 
         // WHEN prox check returns FAR
         mProximitySensor.setLastEvent(new ThresholdSensorEvent(false, 2));
-        captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
+        mHostCallbackCaptor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
         mProximitySensor.alertListeners();
 
         // THEN don't request pulse because the pending pulse was abandoned early
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index e7caf00..69e74d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -85,7 +85,7 @@
         mHandler = mHandlerThread.getThreadHandler();
         mFakeExecutor = new FakeExecutor(new FakeSystemClock());
         mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
-                mDozeParameters, mFakeExecutor, mDozeLog);
+                mHandler, mDozeParameters, mFakeExecutor, mDozeLog);
         mDozeUi.setDozeMachine(mMachine);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
index f07edf3..4253c76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java
@@ -26,6 +26,7 @@
 import android.app.IWallpaperManager;
 import android.os.RemoteException;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -36,11 +37,10 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DozeWallpaperStateTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
index 2bd2bff..771ecaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FakeFeatureFlagsTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.flags
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -25,7 +25,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class FakeFeatureFlagsTest : SysuiTestCase() {
 
     private val unreleasedFlag = UnreleasedFlag("-1000", "test")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
index 91da88e..0ae59bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagDependenciesTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.flags
 
-import android.testing.AndroidTestingRunner
 import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import java.io.PrintWriter
@@ -25,7 +25,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class FlagDependenciesTest : SysuiTestCase() {
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
index 2daa86b..d1082bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/ServerFlagReaderImplTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.flags
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.DeviceConfigProxyFake
@@ -33,7 +33,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class ServerFlagReaderImplTest : SysuiTestCase() {
 
     private val NAMESPACE = "test"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 34b2aaf..c985199 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -103,6 +103,17 @@
         }
 
     @Test
+    fun shouldShow_falseAfterCloseSystemDialogs() =
+        testScope.runTest {
+            val shouldShow by collectLastValue(viewModel.shouldShow)
+
+            testHelper.showFromActivity()
+            testHelper.hideThroughCloseSystemDialogs()
+
+            assertThat(shouldShow).isFalse()
+        }
+
+    @Test
     fun shouldShow_doesNotEmitDuplicateValues() =
         testScope.runTest {
             val shouldShowValues by collectValues(viewModel.shouldShow)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 128dd23..27b9863 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -44,6 +44,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.clearInvocations
 import java.util.function.Predicate
 
 @RunWith(AndroidJUnit4::class)
@@ -336,6 +337,10 @@
                 false /* requestedShowSurfaceBehindKeyguard */
         )
 
+        // Cancel the animator so we can verify only the setSurfaceBehind call below.
+        keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
+        clearInvocations(surfaceTransactionApplier)
+
         // Set appear to 50%, we'll just verify that we're not applying the identity matrix which
         // means an animation is in progress.
         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
@@ -377,6 +382,10 @@
                 false /* requestedShowSurfaceBehindKeyguard */
         )
 
+        // Cancel the animator so we can verify only the setSurfaceBehind call below.
+        keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
+        clearInvocations(surfaceTransactionApplier)
+
         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
         keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
 
@@ -409,6 +418,10 @@
                 false /* requestedShowSurfaceBehindKeyguard */
         )
 
+        // Stop the animator - we just want to test whether the override is not applied.
+        keyguardUnlockAnimationController.surfaceBehindAlphaAnimator.end()
+        clearInvocations(surfaceTransactionApplier)
+
         keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
         keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 6b1d39a..03afcb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -1240,6 +1240,7 @@
         mViewMediator.start();
 
         mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null);
+        mViewMediator.onBootCompleted();
     }
 
     private void captureKeyguardStateControllerCallback() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 7c92ede..42ab25f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -48,6 +48,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
 import com.android.systemui.testKosmos
 import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -78,6 +79,9 @@
     fun setup() {
         underTest.start()
 
+        kosmos.fakeKeyguardRepository.setDreaming(true)
+        kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true)
+
         // Transition to DOZING and set the power interactor asleep.
         powerInteractor.setAsleepForTest()
         runBlocking {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
index 88fe4ce..af76b08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorTest.kt
@@ -16,12 +16,18 @@
 
 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 androidx.test.filters.SmallTest
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.AuthenticationFlags
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -81,6 +87,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionsToLockscreen_ifFinishedInGone() =
         testScope.runTest {
             keyguardTransitionRepository.sendTransitionSteps(
@@ -92,7 +99,34 @@
             kosmos.fakeKeyguardRepository.setKeyguardShowing(true)
             runCurrent()
 
-            // We're in the middle of a LOCKSCREEN -> GONE transition.
+            // We're in the middle of a GONE -> LOCKSCREEN transition.
+            assertThat(keyguardTransitionRepository)
+                .startedTransition(
+                    to = KeyguardState.LOCKSCREEN,
+                )
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun testTransitionsToLockscreen_ifFinishedInGone_wmRefactor() =
+        testScope.runTest {
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GONE,
+                testScope,
+            )
+            reset(keyguardTransitionRepository)
+
+            // Trigger lockdown.
+            kosmos.fakeBiometricSettingsRepository.setAuthenticationFlags(
+                AuthenticationFlags(
+                    0,
+                    LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+                )
+            )
+            runCurrent()
+
+            // We're in the middle of a GONE -> LOCKSCREEN transition.
             assertThat(keyguardTransitionRepository)
                 .startedTransition(
                     to = KeyguardState.LOCKSCREEN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
index c782e9d..459e41d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.util.mockTopActivityClassName
 import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.user.domain.UserDomainLayerModule
 import dagger.BindsInstance
 import dagger.Component
 import junit.framework.Assert.assertEquals
@@ -443,6 +444,7 @@
             [
                 SysUITestModule::class,
                 BiometricsDomainLayerModule::class,
+                UserDomainLayerModule::class,
             ]
     )
     interface TestComponent {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index e02fb29..246cfbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -30,7 +30,6 @@
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.dock.fakeDockManager
 import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.DisableSceneContainer
@@ -570,13 +569,13 @@
             // WHEN biometrics succeeds with wake and unlock mode
             powerInteractor.setAwakeForTest()
             keyguardRepository.setBiometricUnlockState(BiometricUnlockMode.WAKE_AND_UNLOCK)
-            advanceTimeBy(60L)
+            runCurrent()
 
             assertThat(transitionRepository)
                 .startedTransition(
                     to = KeyguardState.GONE,
                     from = KeyguardState.DOZING,
-                    ownerName = "FromDozingTransitionInteractor",
+                    ownerName = "FromDozingTransitionInteractor(biometric wake and unlock)",
                     animatorAssertion = { it.isNotNull() }
                 )
 
@@ -608,31 +607,6 @@
 
     /** This handles security method NONE and screen off with lock timeout */
     @Test
-    fun dozingToGoneWithKeyguardNotShowing() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to DOZING
-            runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING)
-            runCurrent()
-
-            // WHEN the device wakes up without a keyguard
-            keyguardRepository.setKeyguardShowing(false)
-            keyguardRepository.setKeyguardDismissible(true)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(false)
-            powerInteractor.setAwakeForTest()
-            advanceTimeBy(60L)
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    to = KeyguardState.GONE,
-                    from = KeyguardState.DOZING,
-                    animatorAssertion = { it.isNotNull() }
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    /** This handles security method NONE and screen off with lock timeout */
-    @Test
     @DisableSceneContainer
     fun dreamingToGoneWithKeyguardNotShowing() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
index 33e9b36..c7f4416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
 import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.user.domain.UserDomainLayerModule
 import com.android.systemui.util.mockito.any
 import dagger.BindsInstance
 import dagger.Component
@@ -120,6 +121,7 @@
             [
                 SysUITestModule::class,
                 BiometricsDomainLayerModule::class,
+                UserDomainLayerModule::class,
             ]
     )
     interface TestComponent {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 1f13298..4e1b12f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -100,9 +100,11 @@
     }
 
     @Test
-    fun testAodVisible_noLockscreenShownCallYet_defaultsToShowLockscreen() {
+    fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater() {
         underTest.setAodVisible(false)
+        verifyNoMoreInteractions(activityTaskManagerService)
 
+        underTest.setLockscreenShown(true)
         verify(activityTaskManagerService).setLockScreenShown(true, false)
         verifyNoMoreInteractions(activityTaskManagerService)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index 7d4f034..201ee88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.keyguard.ui.view.layout.sections
 
 import android.view.View
+import android.widget.LinearLayout
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.constraintlayout.widget.ConstraintSet.GONE
@@ -58,7 +59,7 @@
 
     private val smartspaceView = View(mContext).also { it.id = sharedR.id.bc_smartspace_view }
     private val weatherView = View(mContext).also { it.id = sharedR.id.weather_smartspace_view }
-    private val dateView = View(mContext).also { it.id = sharedR.id.date_smartspace_view }
+    private val dateView = LinearLayout(mContext).also { it.id = sharedR.id.date_smartspace_view }
     private lateinit var constraintLayout: ConstraintLayout
     private lateinit var constraintSet: ConstraintSet
 
@@ -109,7 +110,7 @@
         whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
         underTest.addViews(constraintLayout)
         assert(smartspaceView.parent == constraintLayout)
-        assert(weatherView.parent == constraintLayout)
+        assertThat(weatherView.parent).isEqualTo(dateView)
         assert(dateView.parent == constraintLayout)
     }
 
@@ -127,7 +128,7 @@
         whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true)
         underTest.addViews(constraintLayout)
         underTest.applyConstraints(constraintSet)
-        assertWeatherSmartspaceConstrains(constraintSet)
+        assertThat(weatherView.parent).isEqualTo(dateView)
 
         val smartspaceConstraints = constraintSet.getConstraint(smartspaceView.id)
         assertThat(smartspaceConstraints.layout.topToBottom).isEqualTo(dateView.id)
@@ -141,7 +142,6 @@
         hasCustomWeatherDataDisplay.value = true
         underTest.addViews(constraintLayout)
         underTest.applyConstraints(constraintSet)
-        assertWeatherSmartspaceConstrains(constraintSet)
 
         val dateConstraints = constraintSet.getConstraint(dateView.id)
         assertThat(dateConstraints.layout.bottomToTop).isEqualTo(smartspaceView.id)
@@ -168,12 +168,4 @@
         assertThat(constraintSet.getVisibility(weatherView.id)).isEqualTo(GONE)
         assertThat(constraintSet.getVisibility(dateView.id)).isEqualTo(GONE)
     }
-
-    private fun assertWeatherSmartspaceConstrains(cs: ConstraintSet) {
-        val weatherConstraints = cs.getConstraint(weatherView.id)
-        assertThat(weatherConstraints.layout.topToTop).isEqualTo(dateView.id)
-        assertThat(weatherConstraints.layout.bottomToBottom).isEqualTo(dateView.id)
-        assertThat(weatherConstraints.layout.startToEnd).isEqualTo(dateView.id)
-        assertThat(weatherConstraints.layout.startMargin).isEqualTo(4)
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 8471fe1..4da56b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -53,8 +53,10 @@
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.any
@@ -100,6 +102,7 @@
     private lateinit var dataPrivateProfile: MediaData
     private val clock = FakeSystemClock()
     private val repository: MediaFilterRepository = kosmos.mediaFilterRepository
+    private val mediaLoadingLogger = kosmos.mockMediaLoadingLogger
 
     @Before
     fun setup() {
@@ -118,6 +121,7 @@
                 logger,
                 mediaFlags,
                 repository,
+                mediaLoadingLogger,
             )
         mediaDataFilter.mediaDataProcessor = mediaDataProcessor
         mediaDataFilter.addListener(listener)
@@ -176,6 +180,8 @@
 
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataMain.instanceId), eq(dataMain.active), anyString())
             assertThat(currentMedia).containsExactly(mediaCommonModel)
         }
 
@@ -190,6 +196,7 @@
 
             verify(listener, never())
                 .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+            verify(mediaLoadingLogger, never()).logMediaLoaded(any(), anyBoolean(), anyString())
             assertThat(currentMedia).doesNotContain(mediaCommonModel)
         }
 
@@ -203,11 +210,14 @@
             // GIVEN a media was removed for main user
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
 
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataMain.instanceId), eq(dataMain.active), anyString())
             assertThat(currentMedia).containsExactly(mediaCommonModel)
 
             mediaDataFilter.onMediaDataRemoved(KEY, false)
 
             verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
+            verify(mediaLoadingLogger).logMediaRemoved(eq(dataMain.instanceId), anyString())
             assertThat(currentMedia).doesNotContain(mediaCommonModel)
         }
 
@@ -221,6 +231,8 @@
             mediaDataFilter.onMediaDataRemoved(KEY, false)
 
             verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
+            verify(mediaLoadingLogger, never())
+                .logMediaRemoved(eq(dataGuest.instanceId), anyString())
             assertThat(currentMedia).isEmpty()
         }
 
@@ -233,6 +245,8 @@
             // GIVEN that we have a media loaded for main user
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
 
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataMain.instanceId), eq(dataMain.active), anyString())
             assertThat(currentMedia).containsExactly(MediaCommonModel.MediaControl(mediaLoaded))
 
             // and we switch to guest user
@@ -240,6 +254,7 @@
 
             // THEN we should remove the main user's media
             verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
+            verify(mediaLoadingLogger).logMediaRemoved(eq(dataMain.instanceId), anyString())
             assertThat(currentMedia).isEmpty()
         }
 
@@ -260,6 +275,10 @@
             // THEN we should add back the guest user media
             verify(listener)
                 .onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataGuest.instanceId), eq(dataGuest.active), anyString())
+
+            reset(mediaLoadingLogger)
 
             // but not the main user's
             verify(listener, never())
@@ -271,6 +290,8 @@
                     anyInt(),
                     anyBoolean()
                 )
+            verify(mediaLoadingLogger, never())
+                .logMediaLoaded(eq(dataMain.instanceId), anyBoolean(), anyString())
             assertThat(currentMedia)
                 .containsExactly(MediaCommonModel.MediaControl(guestLoadedStatesModel))
             assertThat(currentMedia)
@@ -292,6 +313,7 @@
             val mediaLoadedStatesModel = MediaDataLoadingModel.Loaded(dataMain.instanceId)
             // THEN we should remove the private profile media
             verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false))
+            verify(mediaLoadingLogger).logMediaRemoved(eq(dataGuest.instanceId), anyString())
             assertThat(currentMedia)
                 .containsExactly(MediaCommonModel.MediaControl(mediaLoadedStatesModel))
         }
@@ -541,6 +563,8 @@
             assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
+            verify(mediaLoadingLogger)
+                .logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString())
             verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -570,6 +594,9 @@
             verify(listener, never())
                 .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
             verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
+            verify(mediaLoadingLogger, never()).logMediaLoaded(any(), anyBoolean(), anyString())
+            verify(mediaLoadingLogger, never())
+                .logRecommendationLoaded(any(), anyBoolean(), anyString())
             verify(logger, never()).logRecommendationAdded(any(), any())
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -607,6 +634,8 @@
             assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true))
+            verify(mediaLoadingLogger)
+                .logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString())
             verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -641,6 +670,8 @@
                 .isFalse()
             assertThat(hasActiveMedia(selectedUserEntries)).isFalse()
             verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
+            verify(mediaLoadingLogger, never())
+                .logRecommendationLoaded(any(), anyBoolean(), anyString())
             verify(logger, never()).logRecommendationAdded(any(), any())
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -663,10 +694,15 @@
                     true
                 )
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+            repository.setOrderedMedia()
 
             assertThat(currentMedia).containsExactly(controlCommonModel)
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString())
+
+            reset(mediaLoadingLogger)
 
             // AND we get a smartspace signal
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -685,6 +721,10 @@
             verify(listener, never())
                 .onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), anyInt(), anyBoolean())
             verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
+            verify(mediaLoadingLogger, never())
+                .logMediaLoaded(eq(dataCurrent.instanceId), anyBoolean(), anyString())
+            verify(mediaLoadingLogger, never())
+                .logRecommendationLoaded(any(), anyBoolean(), anyString())
             verify(logger, never()).logRecommendationAdded(any(), any())
             verify(logger, never()).logRecommendationActivated(any(), any(), any())
         }
@@ -706,9 +746,12 @@
                     true
                 )
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+            repository.setOrderedMedia()
             assertThat(currentMedia).containsExactly(controlCommonModel)
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString())
 
             // AND we get a smartspace signal
             runCurrent()
@@ -734,8 +777,16 @@
                     eq(100),
                     eq(true)
                 )
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(
+                    eq(dataCurrentAndActive.instanceId),
+                    eq(dataCurrentAndActive.active),
+                    anyString()
+                )
             // Smartspace update shouldn't be propagated for the empty rec list.
             verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
+            verify(mediaLoadingLogger, never())
+                .logRecommendationLoaded(any(), anyBoolean(), anyString())
             verify(logger, never()).logRecommendationAdded(any(), any())
             verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
         }
@@ -760,10 +811,13 @@
                 )
 
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+            repository.setOrderedMedia()
 
             assertThat(currentMedia).containsExactly(controlCommonModel)
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString())
 
             // AND we get a smartspace signal
             runCurrent()
@@ -780,6 +834,12 @@
                     eq(100),
                     eq(true)
                 )
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(
+                    eq(dataCurrentAndActive.instanceId),
+                    eq(dataCurrentAndActive.active),
+                    anyString()
+                )
             assertThat(
                     hasActiveMediaOrRecommendation(
                         selectedUserEntries,
@@ -792,6 +852,8 @@
             assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+            verify(mediaLoadingLogger)
+                .logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString())
             verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID)
             verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID))
         }
@@ -808,6 +870,8 @@
             mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
 
             verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
+            verify(mediaLoadingLogger)
+                .logRecommendationRemoved(eq(SMARTSPACE_KEY), eq(true), anyString())
             assertThat(currentMedia).isEmpty()
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -834,10 +898,13 @@
                 )
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+            repository.setOrderedMedia()
 
             assertThat(currentMedia).containsExactly(controlCommonModel)
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString())
 
             runCurrent()
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -852,10 +919,18 @@
                     eq(100),
                     eq(true)
                 )
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(
+                    eq(dataCurrentAndActive.instanceId),
+                    eq(dataCurrentAndActive.active),
+                    anyString()
+                )
 
             mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
 
             verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
+            verify(mediaLoadingLogger)
+                .logRecommendationRemoved(eq(SMARTSPACE_KEY), eq(true), anyString())
             assertThat(currentMedia).containsExactly(controlCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -886,6 +961,8 @@
 
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+            verify(mediaLoadingLogger)
+                .logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(false), anyString())
             assertThat(currentMedia).containsExactly(recsCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -922,19 +999,27 @@
             // If there is media that was recently played but inactive
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+            repository.setOrderedMedia()
 
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString())
             assertThat(currentMedia).containsExactly(controlCommonModel)
 
+            reset(mediaLoadingLogger)
+
             // And an inactive recommendation is loaded
             mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
             // Smartspace is loaded but the media stays inactive
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+            verify(mediaLoadingLogger)
+                .logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(false), anyString())
             verify(listener, never())
                 .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean())
+            verify(mediaLoadingLogger, never()).logMediaLoaded(any(), anyBoolean(), anyString())
             assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -986,9 +1071,12 @@
             // WHEN we have media that was recently played, but not currently active
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+            repository.setOrderedMedia()
 
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString())
             assertThat(currentMedia).containsExactly(controlCommonModel)
 
             // AND we get a smartspace signal with extra to trigger resume
@@ -1008,6 +1096,12 @@
                     eq(100),
                     eq(true)
                 )
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(
+                    eq(dataCurrentAndActive.instanceId),
+                    eq(dataCurrentAndActive.active),
+                    anyString()
+                )
             assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
             assertThat(
                     hasActiveMediaOrRecommendation(
@@ -1020,6 +1114,8 @@
             // And update the smartspace data state, but not prioritized
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+            verify(mediaLoadingLogger)
+                .logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString())
         }
 
     @Test
@@ -1039,11 +1135,16 @@
             // WHEN we have media that was recently played, but not currently active
             val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
             mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+            repository.setOrderedMedia()
 
             verify(listener)
                 .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false))
+            verify(mediaLoadingLogger)
+                .logMediaLoaded(eq(dataCurrent.instanceId), eq(dataCurrent.active), anyString())
             assertThat(currentMedia).containsExactly(controlCommonModel)
 
+            reset(mediaLoadingLogger)
+
             // AND we get a smartspace signal with extra to not trigger resume
             val extras = Bundle().apply { putBoolean(EXTRA_KEY_TRIGGER_RESUME, false) }
             whenever(cardAction.extras).thenReturn(extras)
@@ -1052,9 +1153,13 @@
             // THEN listeners are not updated to show media
             verify(listener, never())
                 .onMediaDataLoaded(eq(KEY), eq(KEY), any(), eq(true), eq(100), eq(true))
+            verify(mediaLoadingLogger, never())
+                .logMediaLoaded(eq(dataCurrent.instanceId), anyBoolean(), anyString())
             // But the smartspace update is still propagated
             verify(listener)
                 .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false))
+            verify(mediaLoadingLogger)
+                .logRecommendationLoaded(eq(SMARTSPACE_KEY), eq(true), anyString())
             assertThat(currentMedia).containsExactly(controlCommonModel, recsCommonModel)
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 18b4c48..3b541cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -242,7 +242,6 @@
         mediaCarouselInteractor =
             MediaCarouselInteractor(
                 applicationScope = testScope.backgroundScope,
-                mediaDataRepository = mediaDataRepository,
                 mediaDataProcessor = mediaDataProcessor,
                 mediaTimeoutListener = mediaTimeoutListener,
                 mediaResumeListener = mediaResumeListener,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 42bd46f..5142730 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -21,6 +21,7 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
+import android.graphics.drawable.TestStubDrawable
 import android.media.MediaRoute2Info
 import android.media.MediaRouter2Manager
 import android.media.RoutingSessionInfo
@@ -30,6 +31,7 @@
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -89,6 +91,11 @@
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 public class MediaDeviceManagerTest : SysuiTestCase() {
+
+    private companion object {
+        val OTHER_DEVICE_ICON_STUB = TestStubDrawable()
+    }
+
     @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
 
     private lateinit var manager: MediaDeviceManager
@@ -155,6 +162,11 @@
             MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, token = session.sessionToken)
         whenever(controllerFactory.create(session.sessionToken)).thenReturn(controller)
         setupLeAudioConfiguration(false)
+
+        context.orCreateTestableResources.addOverride(
+            R.drawable.ic_media_home_devices,
+            OTHER_DEVICE_ICON_STUB
+        )
     }
 
     @After
@@ -414,6 +426,7 @@
         assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
     }
 
+    @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
     fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
         // GIVEN that MR2Manager returns null for routing session
@@ -429,6 +442,24 @@
         assertThat(data.name).isNull()
     }
 
+    @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+    @Test
+    fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() {
+        // GIVEN that MR2Manager returns null for routing session
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+        // WHEN a notification is added
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+        // THEN the device is disabled and name and icon are set to "OTHER DEVICE".
+        val data = captureDeviceData(KEY)
+        assertThat(data.enabled).isFalse()
+        assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+        assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+    }
+
+    @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
     fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
         // GIVEN a notif is added
@@ -449,7 +480,30 @@
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isNull()
     }
+    @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+    @Test
+    fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_returnOtherDevice() {
+        // GIVEN a notif is added
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+        reset(listener)
+        // AND MR2Manager returns null for routing session
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+        // WHEN the selected device changes state
+        val deviceCallback = captureCallback()
+        deviceCallback.onSelectedDeviceStateChanged(device, 1)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+        // THEN the device is disabled and name and icon are set to "OTHER DEVICE".
+        val data = captureDeviceData(KEY)
+        assertThat(data.enabled).isFalse()
+        assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+        assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+    }
 
+    @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
     fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
         // GIVEN a notif is added
@@ -471,6 +525,29 @@
         assertThat(data.name).isNull()
     }
 
+    @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+    @Test
+    fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() {
+        // GIVEN a notif is added
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+        reset(listener)
+        // GIVEN that MR2Manager returns null for routing session
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+        // WHEN the selected device changes state
+        val deviceCallback = captureCallback()
+        deviceCallback.onDeviceListUpdate(mutableListOf(device))
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+        // THEN device is disabled and name and icon are set to "OTHER DEVICE".
+        val data = captureDeviceData(KEY)
+        assertThat(data.enabled).isFalse()
+        assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
+        assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
+    }
+
     // With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
     @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 6a2637d..ccf926a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -193,11 +193,12 @@
         whenever(panel.mediaViewController).thenReturn(mediaViewController)
         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         MediaPlayerData.clear()
+        FakeExecutor.exhaustExecutors(bgExecutor)
         verify(globalSettings)
-            .registerContentObserverSync(
-                eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
-                capture(settingsObserverCaptor)
-            )
+                .registerContentObserverSync(
+                        eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
+                        capture(settingsObserverCaptor)
+                )
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index bba01bd..6c350cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -25,6 +25,8 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
 import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
 import com.android.systemui.dreams.DreamOverlayStateController
@@ -498,6 +500,8 @@
                 to = KeyguardState.GLANCEABLE_HUB,
                 testScope = testScope,
             )
+            kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+            runCurrent()
             mediaHierarchyManager.qsExpansion = 0f
             mediaHierarchyManager.setTransitionToFullShadeAmount(123f)
 
@@ -542,6 +546,8 @@
                 to = KeyguardState.GLANCEABLE_HUB,
                 testScope = testScope,
             )
+            kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+            runCurrent()
             verify(mediaCarouselController)
                 .onDesiredLocationChanged(
                     eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
@@ -557,6 +563,8 @@
                 to = KeyguardState.LOCKSCREEN,
                 testScope = testScope,
             )
+            kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Blank)
+            runCurrent()
             verify(mediaCarouselController)
                 .onDesiredLocationChanged(
                     eq(MediaHierarchyManager.LOCATION_QQS),
@@ -579,6 +587,8 @@
                 to = KeyguardState.GLANCEABLE_HUB,
                 testScope = testScope,
             )
+            kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+            runCurrent()
             verify(mediaCarouselController)
                 .onDesiredLocationChanged(
                     eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
@@ -600,6 +610,8 @@
                 to = KeyguardState.GLANCEABLE_HUB,
                 testScope = testScope,
             )
+            kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+            runCurrent()
             verify(mediaCarouselController)
                 .onDesiredLocationChanged(
                     eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
@@ -635,6 +647,8 @@
                 to = KeyguardState.GLANCEABLE_HUB,
                 testScope = testScope,
             )
+            kosmos.fakeCommunalSceneRepository.changeScene(CommunalScenes.Communal)
+            runCurrent()
             // Mock the behavior for dreaming that pulling down shade will immediately set QS as
             // expanded
             expandQS()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index ec02c64..411ff91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -113,8 +113,8 @@
                 LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
         mMediaDevices.add(mMediaDevice1);
         mMediaDevices.add(mMediaDevice2);
-        mMediaItems.add(new MediaItem(mMediaDevice1));
-        mMediaItems.add(new MediaItem(mMediaDevice2));
+        mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice1));
+        mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice2));
 
         mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
         mMediaOutputAdapter.updateItems();
@@ -146,7 +146,8 @@
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
-        mMediaItems.add(new MediaItem());
+        mMediaItems.add(MediaItem.createPairNewDeviceMediaItem());
+        mMediaItems.add(MediaItem.createPairNewDeviceMediaItem());
         mMediaOutputAdapter.updateItems();
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
 
@@ -589,7 +590,7 @@
         mMediaOutputAdapter.updateItems();
         mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
                 .onCreateViewHolder(new LinearLayout(mContext), 0);
-        mMediaItems.add(new MediaItem());
+        mMediaItems.add(MediaItem.createPairNewDeviceMediaItem());
         mMediaOutputAdapter.updateItems();
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
         mViewHolder.mContainerLayout.performClick();
@@ -725,7 +726,7 @@
     public void updateItems_controllerItemsUpdated_notUpdatesInAdapterUntilUpdateItems() {
         mMediaOutputAdapter.updateItems();
         List<MediaItem> updatedList = new ArrayList<>();
-        updatedList.add(new MediaItem());
+        updatedList.add(MediaItem.createPairNewDeviceMediaItem());
         when(mMediaOutputController.getMediaItemList()).thenReturn(updatedList);
         assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaItems.size());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
index 53e9dc8..2a8967e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
@@ -62,7 +62,7 @@
         MockitoAnnotations.initMocks(this)
         context.addMockSystemService(Context.AUDIO_SERVICE, audioManager)
         icon = context.getDrawable(R.drawable.ic_cake)!!
-        whenever(deviceIconUtil.getIconFromAudioDeviceType(any(), any())).thenReturn(icon)
+        whenever(deviceIconUtil.getIconFromAudioDeviceType(any())).thenReturn(icon)
 
         muteAwaitConnectionManager = MediaMuteAwaitConnectionManager(
             FakeExecutor(FakeSystemClock()),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index 2f61579..7211620 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -2,6 +2,7 @@
 
 import android.app.ActivityManager.RecentTaskInfo
 import android.content.pm.UserInfo
+import android.graphics.Rect
 import android.os.UserManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -14,8 +15,10 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
 import com.android.wm.shell.recents.RecentTasks
 import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.android.wm.shell.util.SplitBounds
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
 import java.util.function.Consumer
@@ -101,6 +104,17 @@
     }
 
     @Test
+    fun loadRecentTasks_singleTaskPair_returnsTasksAsForeground() {
+        givenRecentTasks(
+            createTaskPair(taskId1 = 2, taskId2 = 3, isVisible = true),
+        )
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result[0].isForegroundTask).isTrue()
+    }
+
+    @Test
     fun loadRecentTasks_multipleTasks_returnsSecondVisibleTaskAsForegroundTask() {
         givenRecentTasks(
             createSingleTask(taskId = 1),
@@ -144,6 +158,21 @@
     }
 
     @Test
+    fun loadRecentTasks_firstTaskIsGroupedAndVisible_marksBothGroupedTasksAsForeground() {
+        givenRecentTasks(
+            createTaskPair(taskId1 = 1, taskId2 = 2, isVisible = true),
+            createSingleTask(taskId = 3),
+            createSingleTask(taskId = 4),
+        )
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result.map { it.isForegroundTask })
+                .containsExactly(true, true, false, false)
+                .inOrder()
+    }
+
+    @Test
     fun loadRecentTasks_secondTaskIsGroupedAndInvisible_marksBothGroupedTasksAsNotForeground() {
         givenRecentTasks(
             createSingleTask(taskId = 1),
@@ -159,6 +188,21 @@
     }
 
     @Test
+    fun loadRecentTasks_firstTaskIsGroupedAndInvisible_marksBothGroupedTasksAsNotForeground() {
+        givenRecentTasks(
+            createTaskPair(taskId1 = 1, taskId2 = 2, isVisible = false),
+            createSingleTask(taskId = 3),
+            createSingleTask(taskId = 4),
+        )
+
+        val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+        assertThat(result.map { it.isForegroundTask })
+                .containsExactly(false, false, false, false)
+                .inOrder()
+    }
+
+    @Test
     fun loadRecentTasks_assignsCorrectUserType() {
         givenRecentTasks(
             createSingleTask(taskId = 1, userId = 10, userType = STANDARD),
@@ -224,7 +268,7 @@
         GroupedRecentTaskInfo.forSplitTasks(
             createTaskInfo(taskId1, userId1, isVisible),
             createTaskInfo(taskId2, userId2, isVisible),
-            null
+            SplitBounds(Rect(), Rect(), taskId1, taskId2, SNAP_TO_50_50)
         )
 
     private fun createTaskInfo(taskId: Int, userId: Int, isVisible: Boolean = false) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index b7fefc0..c0d411b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.mediaprojection.data.repository
 
+import android.media.projection.MediaProjectionInfo
 import android.os.Binder
+import android.os.UserHandle
 import android.view.ContentRecordingSession
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -26,6 +28,7 @@
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken
+import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager.Companion.createDisplaySession
 import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
 import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
 import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
@@ -33,6 +36,7 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -55,7 +59,8 @@
             fakeActivityTaskManager.addRunningTasks(task)
             repo.switchProjectedTask(task)
 
-            assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task))
+            assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java)
+            assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task)
         }
 
     @Test
@@ -97,7 +102,7 @@
                 session = ContentRecordingSession.createDisplaySession(/* displayToMirror= */ 123)
             )
 
-            assertThat(state).isEqualTo(MediaProjectionState.EntireScreen)
+            assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java)
         }
 
     @Test
@@ -110,7 +115,27 @@
                 session = ContentRecordingSession.createTaskSession(taskWindowContainerToken)
             )
 
-            assertThat(state).isEqualTo(MediaProjectionState.EntireScreen)
+            assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java)
+        }
+
+    @Test
+    fun mediaProjectionState_entireScreen_hasHostPackage() =
+        testScope.runTest {
+            val state by collectLastValue(repo.mediaProjectionState)
+
+            val info =
+                MediaProjectionInfo(
+                    /* packageName= */ "com.media.projection.repository.test",
+                    /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()),
+                    /* launchCookie = */ null,
+                )
+            fakeMediaProjectionManager.dispatchOnSessionSet(
+                info = info,
+                session = createDisplaySession(),
+            )
+
+            assertThat((state as MediaProjectionState.Projecting.EntireScreen).hostPackage)
+                .isEqualTo("com.media.projection.repository.test")
         }
 
     @Test
@@ -125,6 +150,39 @@
                 session = ContentRecordingSession.createTaskSession(token.asBinder())
             )
 
-            assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task))
+            assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java)
+            assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task)
+        }
+
+    @Test
+    fun mediaProjectionState_singleTask_hasHostPackage() =
+        testScope.runTest {
+            val state by collectLastValue(repo.mediaProjectionState)
+
+            val token = createToken()
+            val task = createTask(taskId = 1, token = token)
+            fakeActivityTaskManager.addRunningTasks(task)
+
+            val info =
+                MediaProjectionInfo(
+                    /* packageName= */ "com.media.projection.repository.test",
+                    /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()),
+                    /* launchCookie = */ null,
+                )
+            fakeMediaProjectionManager.dispatchOnSessionSet(
+                info = info,
+                session = ContentRecordingSession.createTaskSession(token.asBinder())
+            )
+
+            assertThat((state as MediaProjectionState.Projecting.SingleTask).hostPackage)
+                .isEqualTo("com.media.projection.repository.test")
+        }
+
+    @Test
+    fun stopProjecting_invokesManager() =
+        testScope.runTest {
+            repo.stopProjecting()
+
+            verify(fakeMediaProjectionManager.mediaProjectionManager).stopActiveProjection()
         }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 2d282dc..8aaa121 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -14,6 +14,7 @@
 package com.android.systemui.qs
 
 import android.graphics.Rect
+import android.platform.test.flag.junit.FlagsParameterization
 import android.testing.TestableContext
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
@@ -25,15 +26,16 @@
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.FrameLayout
 import android.widget.LinearLayout
-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.flags.DisableSceneContainer
+import com.android.systemui.flags.parameterizeSceneContainerFlag
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.qs.QSTileView
 import com.android.systemui.qs.QSPanelControllerBase.TileRecord
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileViewImpl
+import com.android.systemui.res.R
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -44,11 +46,17 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @RunWithLooper
 @SmallTest
-class QSPanelTest : SysuiTestCase() {
+class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
 
     @Mock private lateinit var qsLogger: QSLogger
 
@@ -57,9 +65,8 @@
 
     private lateinit var footer: View
 
-    private val themedContext = TestableContext(
-            ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
-    )
+    private val themedContext =
+        TestableContext(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings))
 
     @Before
     @Throws(Exception::class)
@@ -106,38 +113,8 @@
     }
 
     @Test
-    fun testTilesFooterVisibleRTLLandscapeMedia() {
-        qsPanel.layoutDirection = View.LAYOUT_DIRECTION_RTL
-        // We need at least a tile so the layout has a height
-        qsPanel.tileLayout?.addTile(
-                QSPanelControllerBase.TileRecord(
-                    mock(QSTile::class.java),
-                    QSTileViewImpl(themedContext)
-                )
-            )
-
-        val mediaView = FrameLayout(themedContext)
-        mediaView.addView(View(themedContext), MATCH_PARENT, 800)
-
-        qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true)
-        qsPanel.measure(
-            /* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY),
-            /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)
-        )
-        qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight)
-
-        val tiles = qsPanel.tileLayout as View
-        // Tiles are effectively to the right of media
-        assertThat(mediaView isLeftOf tiles)
-        assertThat(tiles.isVisibleToUser).isTrue()
-
-        assertThat(mediaView isLeftOf footer)
-        assertThat(footer.isVisibleToUser).isTrue()
-    }
-
-    @Test
+    @DisableSceneContainer
     fun testTilesFooterVisibleLandscapeMedia() {
-        qsPanel.layoutDirection = View.LAYOUT_DIRECTION_LTR
         // We need at least a tile so the layout has a height
         qsPanel.tileLayout?.addTile(
             QSPanelControllerBase.TileRecord(
@@ -158,10 +135,10 @@
 
         val tiles = qsPanel.tileLayout as View
         // Tiles are effectively to the left of media
-        assertThat(tiles isLeftOf mediaView)
+        assertThat(tiles isLeftOf mediaView).isTrue()
         assertThat(tiles.isVisibleToUser).isTrue()
 
-        assertThat(footer isLeftOf mediaView)
+        assertThat(footer isLeftOf mediaView).isTrue()
         assertThat(footer.isVisibleToUser).isTrue()
     }
 
@@ -169,8 +146,8 @@
     fun testBottomPadding() {
         val padding = 10
         themedContext.orCreateTestableResources.addOverride(
-                R.dimen.qs_panel_padding_bottom,
-                padding
+            R.dimen.qs_panel_padding_bottom,
+            padding
         )
         qsPanel.updatePadding()
         assertThat(qsPanel.paddingBottom).isEqualTo(padding)
@@ -182,8 +159,8 @@
         val paddingCombined = 100
         themedContext.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
         themedContext.orCreateTestableResources.addOverride(
-                R.dimen.qs_panel_padding_top,
-                paddingCombined
+            R.dimen.qs_panel_padding_top,
+            paddingCombined
         )
 
         qsPanel.updatePadding()
@@ -220,7 +197,8 @@
     }
 
     @Test
-    fun initializedWithNoMedia_tileLayoutParentIsAlwaysQsPanel() {
+    @DisableSceneContainer
+    fun initializedWithNoMedia_sceneContainerDisabled_tileLayoutParentIsAlwaysQsPanel() {
         lateinit var panel: QSPanel
         lateinit var tileLayout: View
         testableLooper.runWithLooper {
@@ -249,6 +227,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun initializeWithNoMedia_mediaNeverAttached() {
         lateinit var panel: QSPanel
         testableLooper.runWithLooper {
@@ -288,6 +267,10 @@
         assertThat(qsPanel.tileLayout!!.maxColumns).isEqualTo(2)
     }
 
+    companion object {
+        @Parameters(name = "{0}") @JvmStatic fun getParams() = parameterizeSceneContainerFlag()
+    }
+
     private infix fun View.isLeftOf(other: View): Boolean {
         val rect = Rect()
         getBoundsOnScreen(rect)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index 130aafb..415cc7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -28,11 +28,12 @@
 import android.widget.TextView
 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.haptics.qs.QSLongPressEffect
 import com.android.systemui.haptics.qs.qsLongPressEffect
 import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.qsTileFactory
+import com.android.systemui.res.R
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -46,8 +47,7 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class QSTileViewImplTest : SysuiTestCase() {
 
-    @Mock
-    private lateinit var customDrawable: Drawable
+    @Mock private lateinit var customDrawable: Drawable
 
     private lateinit var tileView: FakeTileView
     private lateinit var customDrawableView: View
@@ -130,9 +130,8 @@
 
         tileView.changeState(state)
 
-        assertThat(state.secondaryLabel as CharSequence).isEqualTo(
-            context.getString(R.string.tile_unavailable)
-        )
+        assertThat(state.secondaryLabel as CharSequence)
+            .isEqualTo(context.getString(R.string.tile_unavailable))
     }
 
     @Test
@@ -143,9 +142,8 @@
 
         tileView.changeState(state)
 
-        assertThat(state.secondaryLabel as CharSequence).isEqualTo(
-            context.getString(R.string.switch_bar_off)
-        )
+        assertThat(state.secondaryLabel as CharSequence)
+            .isEqualTo(context.getString(R.string.switch_bar_off))
     }
 
     @Test
@@ -156,9 +154,8 @@
 
         tileView.changeState(state)
 
-        assertThat(state.secondaryLabel as CharSequence).isEqualTo(
-            context.getString(R.string.switch_bar_on)
-        )
+        assertThat(state.secondaryLabel as CharSequence)
+            .isEqualTo(context.getString(R.string.switch_bar_on))
     }
 
     @Test
@@ -236,11 +233,10 @@
         val offString = "${spec}_off"
         val onString = "${spec}_on"
 
-        context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf(
-            unavailableString,
-            offString,
-            onString
-        ))
+        context.orCreateTestableResources.addOverride(
+            R.array.tile_states_internet,
+            arrayOf(unavailableString, offString, onString)
+        )
 
         // State UNAVAILABLE
         state.secondaryLabel = ""
@@ -342,11 +338,10 @@
     @Test
     fun testDisabledByPolicy_secondaryLabelText() {
         val testA11yLabel = "TEST_LABEL"
-        context.orCreateTestableResources
-                .addOverride(
-                        R.string.accessibility_tile_disabled_by_policy_action_description,
-                        testA11yLabel
-                )
+        context.orCreateTestableResources.addOverride(
+            R.string.accessibility_tile_disabled_by_policy_action_description,
+            testA11yLabel
+        )
 
         val stateDisabledByPolicy = QSTile.State()
         stateDisabledByPolicy.state = Tile.STATE_INACTIVE
@@ -357,10 +352,11 @@
         val info = AccessibilityNodeInfo(tileView)
         tileView.onInitializeAccessibilityNodeInfo(info)
         assertThat(
-                info.actionList.find {
-                        it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id
-                }?.label
-        ).isEqualTo(testA11yLabel)
+                info.actionList
+                    .find { it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id }
+                    ?.label
+            )
+            .isEqualTo(testA11yLabel)
     }
 
     @Test
@@ -375,11 +371,10 @@
         val offString = "${spec}_off"
         val onString = "${spec}_on"
 
-        context.orCreateTestableResources.addOverride(R.array.tile_states_internet, arrayOf(
-                unavailableString,
-                offString,
-                onString
-        ))
+        context.orCreateTestableResources.addOverride(
+            R.array.tile_states_internet,
+            arrayOf(unavailableString, offString, onString)
+        )
 
         tileView.changeState(state)
         assertThat(tileView.stateDescription?.contains(unavailableString)).isTrue()
@@ -412,7 +407,7 @@
     }
 
     @Test
-    fun onStateChange_fromLongPress_to_noLongPress_unBoundsTile() {
+    fun onStateChange_fromLongPress_to_noLongPress_clearsResources() {
         // GIVEN a state that no longer handles long-press
         val state = QSTile.State()
         state.handlesLongClick = false
@@ -420,12 +415,12 @@
         // WHEN the state changes
         tileView.changeState(state)
 
-        // THEN the view binder no longer binds the view to the long-press effect
-        assertThat(tileView.isLongPressEffectBound).isFalse()
+        // THEN the long-press effect resources are not set
+        assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
     }
 
     @Test
-    fun onStateChange_fromNoLongPress_to_longPress_bindsTile() {
+    fun onStateChange_fromNoLongPress_to_longPress_setsProperties() {
         // GIVEN that the tile has changed to a state that does not handle long-press
         val state = QSTile.State()
         state.handlesLongClick = false
@@ -435,12 +430,12 @@
         state.handlesLongClick = true
         tileView.changeState(state)
 
-        // THEN the view is bounded to the long-press effect
-        assertThat(tileView.isLongPressEffectBound).isTrue()
+        // THEN the long-press effect resources are set
+        assertThat(tileView.areLongPressEffectPropertiesSet).isTrue()
     }
 
     @Test
-    fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverBindsEffect() {
+    fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverSetsProperties() {
         // GIVEN a tile where the long-press effect is null
         tileView = FakeTileView(context, false, null)
 
@@ -451,13 +446,13 @@
         // WHEN the state changes
         tileView.changeState(state)
 
-        // THEN the view binder does not bind the view and no effect is initialized
-        assertThat(tileView.isLongPressEffectBound).isFalse()
+        // THEN the effect properties are not set and the effect is not initialized
+        assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
         assertThat(tileView.isLongPressEffectInitialized).isFalse()
     }
 
     @Test
-    fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverBindsEffect() {
+    fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverSetsProperties() {
         // GIVEN a tile where the long-press effect is null
         tileView = FakeTileView(context, false, null)
 
@@ -470,8 +465,8 @@
         state.handlesLongClick = true
         tileView.changeState(state)
 
-        // THEN the view binder does not bind the view and no effect is initialized
-        assertThat(tileView.isLongPressEffectBound).isFalse()
+        // THEN the effect properties are not set and the effect is not initialized
+        assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
         assertThat(tileView.isLongPressEffectInitialized).isFalse()
     }
 
@@ -490,14 +485,15 @@
 
         // THE animation padding corresponds to the tile's growth due to the effect
         val padding = tileView.getPaddingForLaunchAnimation()
-        assertThat(padding).isEqualTo(
-            Rect(
-                -deltaWidth.toInt() / 2,
-                -deltaHeight.toInt() / 2,
-                deltaWidth.toInt() / 2,
-                deltaHeight.toInt() / 2,
+        assertThat(padding)
+            .isEqualTo(
+                Rect(
+                    -deltaWidth.toInt() / 2,
+                    -deltaHeight.toInt() / 2,
+                    deltaWidth.toInt() / 2,
+                    deltaHeight.toInt() / 2,
+                )
             )
-        )
     }
 
     @Test
@@ -536,18 +532,44 @@
         assertThat(tileView.haveLongPressPropertiesBeenReset).isTrue()
     }
 
+    @Test
+    fun onInit_withLongPressEffect_longPressEffectHasTileAndExpandable() {
+        val tile = kosmos.qsTileFactory.createTile("Test Tile")
+        tileView.init(tile)
+
+        assertThat(tileView.isTileAddedToLongPress).isTrue()
+        assertThat(tileView.isExpandableAddedToLongPress).isTrue()
+    }
+
+    @Test
+    fun onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandable() {
+        tileView = FakeTileView(context, false, null)
+        val tile = kosmos.qsTileFactory.createTile("Test Tile")
+        tileView.init(tile)
+
+        assertThat(tileView.isTileAddedToLongPress).isFalse()
+        assertThat(tileView.isExpandableAddedToLongPress).isFalse()
+    }
+
     class FakeTileView(
         context: Context,
         collapsed: Boolean,
-        longPressEffect: QSLongPressEffect?,
-    ) : QSTileViewImpl(
+        private val longPressEffect: QSLongPressEffect?,
+    ) :
+        QSTileViewImpl(
             ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
             collapsed,
             longPressEffect,
-    ) {
+        ) {
         var constantLongPressEffectDuration = 500
+        val isTileAddedToLongPress: Boolean
+            get() = longPressEffect?.qsTile != null
+
+        val isExpandableAddedToLongPress: Boolean
+            get() = longPressEffect?.expandable != null
 
         override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration
+
         fun changeState(state: QSTile.State) {
             handleStateChanged(state)
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 50cf5cc5..9f12b18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -39,7 +39,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.keyguard.TestScopeProvider;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.classifier.FalsingManagerFake;
@@ -54,14 +53,11 @@
 import com.android.systemui.statusbar.connectivity.SignalCallback;
 import com.android.systemui.statusbar.connectivity.WifiIndicators;
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository;
-import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
-import kotlinx.coroutines.test.TestScope;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -105,12 +101,10 @@
     @Mock
     private QsEventLogger mUiEventLogger;
 
-    private WifiInteractor mWifiInteractor;
     private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter();
     private final FakeConnectivityRepository mConnectivityRepository =
             new FakeConnectivityRepository();
     private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
-    private final TestScope mTestScope = TestScopeProvider.getTestScope();
 
     private TestableLooper mTestableLooper;
     private CastTile mCastTile;
@@ -172,8 +166,7 @@
     @Test
     public void testStateActive_wifiEnabledAndCasting() {
         createAndStartTileOldImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastController.CastDevice.STATE_CONNECTED;
+        CastDevice device = createConnectedCastDevice();
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -232,8 +225,7 @@
     @Test
     public void stateActive_wifiConnectedAndCasting_newPipeline() {
         createAndStartTileNewImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastDevice.STATE_CONNECTED;
+        CastDevice device = createConnectedCastDevice();
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -248,8 +240,7 @@
     @Test
     public void stateActive_ethernetConnectedAndCasting_newPipeline() {
         createAndStartTileNewImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastDevice.STATE_CONNECTED;
+        CastDevice device = createConnectedCastDevice();
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -286,8 +277,7 @@
     @Test
     public void testStateActive_hotspotEnabledAndConnectedAndCasting() {
         createAndStartTileOldImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastController.CastDevice.STATE_CONNECTED;
+        CastDevice device = createConnectedCastDevice();
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -309,9 +299,13 @@
     @Test
     public void testHandleClick_castDevicePresent() {
         createAndStartTileOldImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastDevice.STATE_CONNECTED;
-        device.tag = mock(MediaRouter.RouteInfo.class);
+        CastDevice device = new CastDevice(
+                "id",
+                /* name= */ null,
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaRouter,
+                /* tag= */ mock(MediaRouter.RouteInfo.class));
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -327,9 +321,13 @@
     @Test
     public void testHandleClick_projectionOnly() {
         createAndStartTileOldImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastDevice.STATE_CONNECTED;
-        device.tag = mock(MediaProjectionInfo.class);
+        CastDevice device = new CastDevice(
+                "id",
+                /* name= */ null,
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaProjection,
+                /* tag= */ mock(MediaProjectionInfo.class));
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -344,31 +342,40 @@
     @Test
     public void testUpdateState_projectionOnly() {
         createAndStartTileOldImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastDevice.STATE_CONNECTED;
-        device.tag = mock(MediaProjectionInfo.class);
-        device.name = "Test Projection Device";
+        CastDevice device = new CastDevice(
+                "id",
+                /* name= */ "Test Projection Device",
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaProjection,
+                /* tag= */ mock(MediaProjectionInfo.class));
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
 
         enableWifiAndProcessMessages();
         assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
-        assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(device.name));
+        assertTrue(mCastTile.getState().secondaryLabel.toString()
+                .startsWith("Test Projection Device"));
     }
 
     @Test
     public void testUpdateState_castingAndProjection() {
         createAndStartTileOldImpl();
-        CastController.CastDevice casting = new CastController.CastDevice();
-        casting.state = CastDevice.STATE_CONNECTED;
-        casting.tag = mock(RouteInfo.class);
-        casting.name = "Test Casting Device";
-
-        CastController.CastDevice projection = new CastController.CastDevice();
-        projection.state = CastDevice.STATE_CONNECTED;
-        projection.tag = mock(MediaProjectionInfo.class);
-        projection.name = "Test Projection Device";
+        CastDevice casting = new CastDevice(
+                "id1",
+                /* name= */ "Test Casting Device",
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaRouter,
+                /* tag= */ mock(RouteInfo.class));
+        CastDevice projection = new CastDevice(
+                "id2",
+                /* name= */ "Test Projection Device",
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaProjection,
+                /* tag= */ mock(MediaProjectionInfo.class));
 
         List<CastDevice> devices = new ArrayList<>();
         devices.add(casting);
@@ -379,22 +386,27 @@
 
         // Note here that the tile should be active, and should choose casting over projection.
         assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
-        assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(casting.name));
+        assertTrue(mCastTile.getState().secondaryLabel.toString()
+                .startsWith("Test Casting Device"));
     }
 
     @Test
     public void testUpdateState_connectedAndConnecting() {
         createAndStartTileOldImpl();
-        CastController.CastDevice connecting = new CastController.CastDevice();
-        connecting.state = CastDevice.STATE_CONNECTING;
-        connecting.tag = mock(RouteInfo.class);
-        connecting.name = "Test Casting Device";
-
-        CastController.CastDevice connected = new CastController.CastDevice();
-        connected.state = CastDevice.STATE_CONNECTED;
-        connected.tag = mock(RouteInfo.class);
-        connected.name = "Test Casting Device";
-
+        CastDevice connecting = new CastDevice(
+                "id",
+                /* name= */ "Test Connecting Device",
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connecting,
+                /* origin= */ CastDevice.CastOrigin.MediaRouter,
+                /* tag= */ mock(RouteInfo.class));
+        CastDevice connected = new CastDevice(
+                "id",
+                /* name= */ "Test Connected Device",
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaRouter,
+                /* tag= */ mock(RouteInfo.class));
         List<CastDevice> devices = new ArrayList<>();
         devices.add(connecting);
         devices.add(connected);
@@ -404,7 +416,8 @@
 
         // Tile should be connected and always prefer the connected device.
         assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
-        assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
+        assertTrue(mCastTile.getState().secondaryLabel.toString()
+                .startsWith("Test Connected Device"));
     }
 
     @Test
@@ -427,8 +440,7 @@
     @Test
     public void testExpandView_casting_projection() {
         createAndStartTileOldImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastController.CastDevice.STATE_CONNECTED;
+        CastDevice device = createConnectedCastDevice();
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -441,9 +453,14 @@
     @Test
     public void testExpandView_connecting_projection() {
         createAndStartTileOldImpl();
-        CastController.CastDevice connecting = new CastController.CastDevice();
-        connecting.state = CastDevice.STATE_CONNECTING;
-        connecting.name = "Test Casting Device";
+        CastDevice connecting = new CastDevice(
+                "id",
+                /* name= */
+                "Test Projection Device",
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaProjection,
+                /* tag= */ mock(MediaProjectionInfo.class));
 
         List<CastDevice> devices = new ArrayList<>();
         devices.add(connecting);
@@ -457,9 +474,14 @@
     @Test
     public void testExpandView_casting_mediaRoute() {
         createAndStartTileOldImpl();
-        CastController.CastDevice device = new CastController.CastDevice();
-        device.state = CastDevice.STATE_CONNECTED;
-        device.tag = mock(MediaRouter.RouteInfo.class);
+        CastDevice device = new CastDevice(
+                "id",
+                /* name= */ "Test Router Device",
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaRouter,
+                /* tag= */ mock(RouteInfo.class));
+
         List<CastDevice> devices = new ArrayList<>();
         devices.add(device);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -472,11 +494,13 @@
     @Test
     public void testExpandView_connecting_mediaRoute() {
         createAndStartTileOldImpl();
-        CastController.CastDevice connecting = new CastController.CastDevice();
-        connecting.state = CastDevice.STATE_CONNECTING;
-        connecting.tag = mock(RouteInfo.class);
-        connecting.name = "Test Casting Device";
-
+        CastDevice connecting = new CastDevice(
+                "id",
+                /* name= */ "Test Router Device",
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connecting,
+                /* origin= */ CastDevice.CastOrigin.MediaRouter,
+                /* tag= */ mock(RouteInfo.class));
         List<CastDevice> devices = new ArrayList<>();
         devices.add(connecting);
         when(mController.getCastDevices()).thenReturn(devices);
@@ -567,4 +591,14 @@
                 hotspotCallbackArgumentCaptor.capture());
         mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
     }
+
+    private CastDevice createConnectedCastDevice() {
+        return new CastDevice(
+                "id",
+                /* name= */ null,
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaProjection,
+                /* tag= */ null);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
index 59ee0b8..76c8cf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
@@ -28,6 +28,7 @@
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.testing.TestableLooper;
 import android.view.View;
 
@@ -37,14 +38,18 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker;
 import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager;
 import com.android.systemui.animation.Expandable;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.res.R;
+import com.android.systemui.statusbar.policy.BluetoothController;
 
 import org.junit.After;
 import org.junit.Before;
@@ -78,7 +83,11 @@
     @Mock
     private QSLogger mQSLogger;
     @Mock
+    private HearingDevicesChecker mDevicesChecker;
+    @Mock
     HearingDevicesDialogManager mHearingDevicesDialogManager;
+    @Mock
+    BluetoothController mBluetoothController;
 
     private TestableLooper mTestableLooper;
     private HearingDevicesTile mTile;
@@ -98,7 +107,9 @@
                 mStatusBarStateController,
                 mActivityStarter,
                 mQSLogger,
-                mHearingDevicesDialogManager);
+                mHearingDevicesDialogManager,
+                mDevicesChecker,
+                mBluetoothController);
 
         mTile.initialize();
         mTestableLooper.processAllMessages();
@@ -142,4 +153,41 @@
 
         verify(mHearingDevicesDialogManager).showDialog(expandable);
     }
+
+    @Test
+    public void handleUpdateState_activeHearingDevice_stateActiveConnectedLabel() {
+        when(mDevicesChecker.isAnyActiveHearingDevice()).thenReturn(true);
+        when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(true);
+
+        BooleanState activeState = new BooleanState();
+        mTile.handleUpdateState(activeState, null);
+
+        assertThat(activeState.state).isEqualTo(Tile.STATE_ACTIVE);
+        assertThat(activeState.secondaryLabel.toString()).isEqualTo(
+                mContext.getString(R.string.quick_settings_hearing_devices_connected));
+    }
+
+    @Test
+    public void handleUpdateState_bondedInactiveHearingDevice_stateInactiveDisconnectedLabel() {
+        when(mDevicesChecker.isAnyActiveHearingDevice()).thenReturn(false);
+        when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(true);
+
+        BooleanState disconnectedState = new BooleanState();
+        mTile.handleUpdateState(disconnectedState, null);
+
+        assertThat(disconnectedState.state).isEqualTo(Tile.STATE_INACTIVE);
+        assertThat(disconnectedState.secondaryLabel.toString()).isEqualTo(
+                mContext.getString(R.string.quick_settings_hearing_devices_disconnected));
+    }
+
+    @Test
+    public void handleUpdateState_noHearingDevice_stateInactive() {
+        when(mDevicesChecker.isAnyActiveHearingDevice()).thenReturn(false);
+        when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(false);
+
+        BooleanState inactiveState = new BooleanState();
+        mTile.handleUpdateState(inactiveState, null);
+
+        assertThat(inactiveState.state).isEqualTo(Tile.STATE_INACTIVE);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index df59e57..73548ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -72,13 +72,13 @@
     @Mock private lateinit var dialogLauncherAnimator: DialogTransitionAnimator
     @Mock private lateinit var panelInteractor: PanelInteractor
     @Mock private lateinit var userContextProvider: UserContextProvider
+    @Mock private lateinit var issueRecordingState: IssueRecordingState
     @Mock private lateinit var traceurMessageSender: TraceurMessageSender
     @Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory
     @Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate
     @Mock private lateinit var dialog: SystemUIDialog
 
     private lateinit var testableLooper: TestableLooper
-    private val issueRecordingState = IssueRecordingState()
     private lateinit var tile: RecordIssueTile
 
     @Before
@@ -114,7 +114,7 @@
 
     @Test
     fun qsTileUi_shouldLookCorrect_whenInactive() {
-        issueRecordingState.isRecording = false
+        whenever(issueRecordingState.isRecording).thenReturn(false)
 
         val testState = tile.newTileState()
         tile.handleUpdateState(testState, null)
@@ -126,7 +126,7 @@
 
     @Test
     fun qsTileUi_shouldLookCorrect_whenRecording() {
-        issueRecordingState.isRecording = true
+        whenever(issueRecordingState.isRecording).thenReturn(true)
         val testState = tile.newTileState()
         tile.handleUpdateState(testState, null)
 
@@ -137,7 +137,7 @@
 
     @Test
     fun inActiveQsTile_switchesToActive_whenClicked() {
-        issueRecordingState.isRecording = false
+        whenever(issueRecordingState.isRecording).thenReturn(false)
 
         val testState = tile.newTileState()
         tile.handleUpdateState(testState, null)
@@ -147,7 +147,7 @@
 
     @Test
     fun activeQsTile_switchesToInActive_whenClicked() {
-        issueRecordingState.isRecording = true
+        whenever(issueRecordingState.isRecording).thenReturn(true)
 
         val testState = tile.newTileState()
         tile.handleUpdateState(testState, null)
@@ -157,7 +157,7 @@
 
     @Test
     fun showPrompt_shouldUseKeyguardDismissUtil_ToShowDialog() {
-        issueRecordingState.isRecording = false
+        whenever(issueRecordingState.isRecording).thenReturn(false)
 
         tile.handleClick(null)
         testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index fc74586..6e6e311 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -241,7 +241,6 @@
             statusBarWinController,
             sysUiState,
             mock(),
-            mock(),
             userTracker,
             wakefulnessLifecycle,
             uiEventLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index ca60650..503c52f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.recordissue
 
 import android.app.Dialog
-import android.content.Context
 import android.content.SharedPreferences
 import android.os.UserHandle
 import android.testing.TestableLooper
@@ -35,9 +34,8 @@
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate
 import com.android.systemui.model.SysUiState
-import com.android.systemui.qs.tiles.RecordIssueTile
+import com.android.systemui.recordissue.IssueRecordingState.Companion.ISSUE_TYPE_NOT_SET
 import com.android.systemui.res.R
-import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.SystemUIDialogManager
@@ -72,7 +70,7 @@
     @Mock private lateinit var dprLazy: dagger.Lazy<ScreenCaptureDevicePolicyResolver>
     @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
     @Mock private lateinit var userTracker: UserTracker
-    @Mock private lateinit var userFileManager: UserFileManager
+    @Mock private lateinit var state: IssueRecordingState
     @Mock private lateinit var sharedPreferences: SharedPreferences
     @Mock
     private lateinit var screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate
@@ -90,7 +88,6 @@
     private lateinit var dialog: SystemUIDialog
     private lateinit var factory: SystemUIDialog.Factory
     private lateinit var latch: CountDownLatch
-    private var issueRecordingState = IssueRecordingState()
 
     @Before
     fun setup() {
@@ -99,14 +96,7 @@
         whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
         whenever(screenCaptureDisabledDialogDelegate.createSysUIDialog())
             .thenReturn(screenCaptureDisabledDialog)
-        whenever(
-                userFileManager.getSharedPreferences(
-                    eq(RecordIssueTile.TILE_SPEC),
-                    eq(Context.MODE_PRIVATE),
-                    anyInt()
-                )
-            )
-            .thenReturn(sharedPreferences)
+        whenever(state.issueTypeRes).thenReturn(ISSUE_TYPE_NOT_SET)
 
         factory =
             spy(
@@ -129,9 +119,8 @@
                     mainExecutor,
                     dprLazy,
                     mediaProjectionMetricsLogger,
-                    userFileManager,
                     screenCaptureDisabledDialogDelegate,
-                    issueRecordingState,
+                    state,
                     traceurMessageSender
                 ) {
                     latch.countDown()
@@ -190,8 +179,7 @@
         whenever(devicePolicyResolver.isScreenCaptureCompletelyDisabled(any<UserHandle>()))
             .thenReturn(false)
         whenever(flags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING)).thenReturn(true)
-        whenever(sharedPreferences.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false))
-            .thenReturn(false)
+        whenever(state.hasUserApprovedScreenRecording).thenReturn(false)
 
         val screenRecordSwitch = dialog.requireViewById<Switch>(R.id.screenrecord_switch)
         screenRecordSwitch.isChecked = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
index b77a15b..61ea437 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt
@@ -119,4 +119,12 @@
 
             assertThat(lastModel).isEqualTo(isRecording)
         }
+
+    @Test
+    fun stopRecording_invokesController() =
+        testScope.runTest {
+            underTest.stopRecording()
+
+            verify(recordingController).stopRecording()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
index 16091b2..1538c72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
@@ -19,6 +19,7 @@
 import android.content.ComponentName
 import android.content.pm.ActivityInfo
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.MATCH_ANY_USER
 import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
 import android.testing.AndroidTestingRunner
 import android.view.Display
@@ -169,12 +170,12 @@
 
     private class ComponentInfoFlagMatcher(
         @PackageManager.ComponentInfoFlagsBits val mask: Int, val value: Int
-    ): ArgumentMatcher<PackageManager.ComponentInfoFlags> {
+    ) : ArgumentMatcher<PackageManager.ComponentInfoFlags> {
         override fun matches(flags: PackageManager.ComponentInfoFlags?): Boolean {
             return flags != null && (mask.toLong() and flags.value) == value.toLong()
         }
 
-        override fun toString(): String{
+        override fun toString(): String {
             return "mask 0x%08x == 0x%08x".format(mask, value)
         }
     }
@@ -191,16 +192,16 @@
         whenever(
             packageManager.getActivityInfo(
                 eq(component),
-                argThat(includesFlagBits(MATCH_DISABLED_COMPONENTS))
+                argThat(includesFlagBits(MATCH_DISABLED_COMPONENTS or MATCH_ANY_USER))
             )
-        ).thenReturn(activityInfo);
+        ).thenReturn(activityInfo)
 
         whenever(
             packageManager.getActivityInfo(
                 eq(component),
                 argThat(excludesFlagBits(MATCH_DISABLED_COMPONENTS))
             )
-        ).thenThrow(PackageManager.NameNotFoundException::class.java);
+        ).thenThrow(PackageManager.NameNotFoundException::class.java)
 
         whenever(windowManager.notifyScreenshotListeners(eq(Display.DEFAULT_DISPLAY)))
             .thenReturn(listOf(component))
@@ -212,5 +213,4 @@
         assertEquals(1, list.size)
         assertEquals(appName, list[0])
     }
-
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
index bf7d909..dd6ba90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -3,6 +3,8 @@
 import android.content.ComponentName
 import android.graphics.Bitmap
 import android.net.Uri
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.view.Display
 import android.view.Display.TYPE_EXTERNAL
@@ -15,6 +17,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.util.ScreenshotRequest
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.display.data.repository.display
@@ -77,6 +80,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_severalDisplays_callsControllerForEachOne() =
         testScope.runTest {
             val internalDisplay = display(TYPE_INTERNAL, id = 0)
@@ -108,6 +112,32 @@
         }
 
     @Test
+    @EnableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
+    fun executeScreenshots_severalDisplaysShelfUi_justCallsOne() =
+        testScope.runTest {
+            val internalDisplay = display(TYPE_INTERNAL, id = 0)
+            val externalDisplay = display(TYPE_EXTERNAL, id = 1)
+            setDisplays(internalDisplay, externalDisplay)
+            val onSaved = { _: Uri? -> }
+            screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+            verify(controllerFactory).create(eq(internalDisplay), any())
+
+            val capturer = ArgumentCaptor<ScreenshotData>()
+
+            verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+            assertThat(capturer.value.displayId).isEqualTo(0)
+
+            assertThat(eventLogger.numLogs()).isEqualTo(1)
+            assertThat(eventLogger.get(0).eventId)
+                .isEqualTo(ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id)
+            assertThat(eventLogger.get(0).packageName).isEqualTo(topComponent.packageName)
+
+            screenshotExecutor.onDestroy()
+        }
+
+    @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_providedImageType_callsOnlyDefaultDisplayController() =
         testScope.runTest {
             val internalDisplay = display(TYPE_INTERNAL, id = 0)
@@ -139,6 +169,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_onlyVirtualDisplays_noInteractionsWithControllers() =
         testScope.runTest {
             setDisplays(display(TYPE_VIRTUAL, id = 0), display(TYPE_VIRTUAL, id = 1))
@@ -150,6 +181,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_allowedTypes_allCaptured() =
         testScope.runTest {
             whenever(controllerFactory.create(any(), any())).thenReturn(controller0)
@@ -168,6 +200,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_reportsOnFinishedOnlyWhenBothFinished() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -193,6 +226,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_oneFinishesOtherFails_reportFailsOnlyAtTheEnd() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -220,6 +254,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_allDisplaysFail_reportsFail() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -247,6 +282,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun onDestroy_propagatedToControllers() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -259,6 +295,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun removeWindows_propagatedToControllers() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -273,6 +310,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun onCloseSystemDialogsReceived_propagatedToControllers() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -287,6 +325,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun onCloseSystemDialogsReceived_someControllerHavePendingTransitions() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -303,6 +342,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_controllerCalledWithRequestProcessorReturnValue() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0))
@@ -324,6 +364,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_errorFromProcessor_logsScreenshotRequested() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -341,6 +382,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_errorFromProcessor_logsUiError() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -358,6 +400,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_errorFromProcessorOnDefaultDisplay_showsErrorNotification() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -384,6 +427,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_errorFromScreenshotController_reportsRequested() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -404,6 +448,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_errorFromScreenshotController_reportsError() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
@@ -424,6 +469,7 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_SCREENSHOT_SHELF_UI2)
     fun executeScreenshots_errorFromScreenshotController_showsErrorNotification() =
         testScope.runTest {
             setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
new file mode 100644
index 0000000..4945ace
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/PolicyRequestProcessorTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot.policy
+
+import android.content.ComponentName
+import android.graphics.Insets
+import android.graphics.Rect
+import android.os.UserHandle
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import com.android.systemui.screenshot.ImageCapture
+import com.android.systemui.screenshot.ScreenshotData
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.TaskSpec
+import com.android.systemui.screenshot.data.model.DisplayContentScenarios.singleFullScreen
+import com.android.systemui.screenshot.data.repository.DisplayContentRepository
+import com.android.systemui.screenshot.policy.TestUserIds.PERSONAL
+import com.android.systemui.screenshot.policy.TestUserIds.WORK
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+
+class PolicyRequestProcessorTest {
+
+    val imageCapture = object : ImageCapture {
+        override fun captureDisplay(displayId: Int, crop: Rect?) = null
+        override suspend fun captureTask(taskId: Int) = null
+    }
+
+    /** Tests behavior when no policies are applied */
+    @Test
+    fun testProcess_defaultOwner_whenNoPolicyApplied() {
+        val fullScreenWork = DisplayContentRepository {
+            singleFullScreen(TaskSpec(taskId = 1001, name = FILES, userId = WORK))
+        }
+
+        val request =
+            ScreenshotData(TAKE_SCREENSHOT_FULLSCREEN,
+                SCREENSHOT_KEY_CHORD,
+                null,
+                topComponent = null,
+                screenBounds = Rect(0, 0, 1, 1),
+                taskId = -1,
+                insets = Insets.NONE,
+                bitmap = null,
+                displayId = DEFAULT_DISPLAY)
+
+        /* Create a policy request processor with no capture policies */
+        val requestProcessor =
+            PolicyRequestProcessor(Dispatchers.Unconfined,
+                imageCapture,
+                policies = emptyList(),
+                defaultOwner = UserHandle.of(PERSONAL),
+                defaultComponent = ComponentName("default", "Component"),
+                displayTasks = fullScreenWork)
+
+        val result = runBlocking { requestProcessor.process(request) }
+
+        assertWithMessage(
+            "With no policy, the screenshot should be assigned to the default user"
+        ).that(result.userHandle).isEqualTo(UserHandle.of(PERSONAL))
+
+        assertWithMessage("The topComponent of the screenshot").that(result.topComponent)
+                .isEqualTo(ComponentName.unflattenFromString(FILES))
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 6536405..13d44de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -18,7 +18,6 @@
 
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
-import static com.android.systemui.Flags.FLAG_SHADE_COLLAPSE_ACTIVITY_LAUNCH_FIX;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPENING;
@@ -48,7 +47,6 @@
 import android.graphics.Point;
 import android.os.PowerManager;
 import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
@@ -679,32 +677,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_SHADE_COLLAPSE_ACTIVITY_LAUNCH_FIX)
-    public void testCanBeCollapsed_expandedInKeyguard() {
-        mStatusBarStateController.setState(KEYGUARD);
-        mNotificationPanelViewController.setExpandedFraction(1f);
-
-        assertThat(mNotificationPanelViewController.canBeCollapsed()).isFalse();
-    }
-
-    @Test
-    @EnableFlags(FLAG_SHADE_COLLAPSE_ACTIVITY_LAUNCH_FIX)
-    public void testCanBeCollapsed_expandedInShade() {
-        mStatusBarStateController.setState(SHADE);
-        mNotificationPanelViewController.setExpandedFraction(1f);
-        assertThat(mNotificationPanelViewController.canBeCollapsed()).isTrue();
-    }
-
-    @Test
-    @DisableFlags(FLAG_SHADE_COLLAPSE_ACTIVITY_LAUNCH_FIX)
-    public void testCanBeCollapsed_expandedInKeyguard_flagDisabled() {
-        mStatusBarStateController.setState(KEYGUARD);
-        mNotificationPanelViewController.setExpandedFraction(1f);
-
-        assertThat(mNotificationPanelViewController.canBeCollapsed()).isTrue();
-    }
-
-    @Test
     @Ignore("b/341163515 - fails to clean up animators correctly")
     public void testSwipeWhileLocked_notifiesKeyguardState() {
         mStatusBarStateController.setState(KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 4a867a8..74a2999 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -70,6 +70,7 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.mockito.any
@@ -85,6 +86,7 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Answers
 import org.mockito.ArgumentCaptor
 import org.mockito.Mock
 import org.mockito.Mockito.anyFloat
@@ -132,6 +134,8 @@
     private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
     @Mock private lateinit var notificationInsetsController: NotificationInsetsController
     @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent
     @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
     @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
     @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@@ -209,6 +213,7 @@
                 dozeScrimController,
                 notificationShadeWindowController,
                 unfoldTransitionProgressProvider,
+                Optional.of(sysUiUnfoldComponent),
                 keyguardUnlockAnimationController,
                 notificationInsetsController,
                 ambientState,
@@ -519,6 +524,46 @@
     }
 
     @Test
+    fun handleExternalTouch_intercepted_sendsOnTouch() {
+        // Accept dispatch and also intercept.
+        whenever(view.dispatchTouchEvent(any())).thenReturn(true)
+        whenever(view.onInterceptTouchEvent(any())).thenReturn(true)
+
+        underTest.handleExternalTouch(DOWN_EVENT)
+        underTest.handleExternalTouch(MOVE_EVENT)
+
+        // Once intercepted, both events are sent to the view.
+        verify(view).onTouchEvent(DOWN_EVENT)
+        verify(view).onTouchEvent(MOVE_EVENT)
+    }
+
+    @Test
+    fun handleExternalTouch_notDispatched_interceptNotCalled() {
+        // Don't accept dispatch
+        whenever(view.dispatchTouchEvent(any())).thenReturn(false)
+
+        underTest.handleExternalTouch(DOWN_EVENT)
+
+        // Interception is not offered.
+        verify(view, never()).onInterceptTouchEvent(any())
+    }
+
+    @Test
+    fun handleExternalTouch_notIntercepted_onTouchNotSent() {
+        // Accept dispatch, but don't dispatch
+        whenever(view.dispatchTouchEvent(any())).thenReturn(true)
+        whenever(view.onInterceptTouchEvent(any())).thenReturn(false)
+
+        underTest.handleExternalTouch(DOWN_EVENT)
+        underTest.handleExternalTouch(MOVE_EVENT)
+
+        // Interception offered for both events, but onTouchEvent is never called.
+        verify(view).onInterceptTouchEvent(DOWN_EVENT)
+        verify(view).onInterceptTouchEvent(MOVE_EVENT)
+        verify(view, never()).onTouchEvent(any())
+    }
+
+    @Test
     fun testGetKeyguardMessageArea() =
         testScope.runTest {
             underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index e83a46b..31bd12f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -58,6 +58,7 @@
 import com.android.systemui.statusbar.phone.DozeServiceHost
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
+import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
@@ -112,6 +113,7 @@
     @Mock private lateinit var shadeLogger: ShadeLogger
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
+    @Mock private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent
     @Mock
     private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
     @Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@@ -181,6 +183,7 @@
                 dozeScrimController,
                 notificationShadeWindowController,
                 unfoldTransitionProgressProvider,
+                Optional.of(sysUiUnfoldComponent),
                 keyguardUnlockAnimationController,
                 notificationInsetsController,
                 ambientState,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
index a05a23b..293dc04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
@@ -27,6 +27,9 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
 
@@ -78,6 +81,22 @@
     }
 
     @Test
+    fun initMultipleTimes_onTransition_translationIsSetOnlyOnce() {
+        animator.init(parent, MAX_TRANSLATION)
+        animator.init(parent, MAX_TRANSLATION)
+        animator.init(parent, MAX_TRANSLATION)
+
+        // GIVEN one view with a matching id
+        val view = spy(View(context))
+        whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(view)
+        progressProvider.onTransitionStarted()
+
+        // WHEN the transition progresses, translation is updated once
+        progressProvider.onTransitionProgress(.5f)
+        verify(view).translationX = anyFloat()
+    }
+
+    @Test
     fun onTransition_oneMovesStartWithRTL() {
         // GIVEN one view with a matching id
         val view = View(context)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
index 2b3f139..6ad8b8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -18,12 +18,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import static java.util.Collections.singletonList;
+
+import android.annotation.Nullable;
 import android.app.Dialog;
 import android.graphics.drawable.Icon;
 import android.os.Handler;
@@ -44,11 +48,13 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
+import org.mockito.stubbing.Answer;
 
 import java.util.Arrays;
-import java.util.Collections;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -56,7 +62,7 @@
 
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
 
-    private static int DEVICE_ID = 1;
+    private static final int DEVICE_ID = 1;
     private KeyboardShortcuts mKeyboardShortcuts;
 
     @Mock private Dialog mDialog;
@@ -66,26 +72,35 @@
     @Before
     public void setUp() {
         mKeyboardShortcuts = new KeyboardShortcuts(mContext, mWindowManager);
-        mKeyboardShortcuts.sInstance = mKeyboardShortcuts;
+        KeyboardShortcuts.sInstance = mKeyboardShortcuts;
         mKeyboardShortcuts.mKeyboardShortcutsDialog = mDialog;
         mKeyboardShortcuts.mContext = mContext;
         mKeyboardShortcuts.mBackgroundHandler = mHandler;
+        when(mHandler.post(any()))
+                .thenAnswer(
+                        new Answer<>() {
+                            @Override
+                            public Object answer(InvocationOnMock invocation) {
+                                ((Runnable) invocation.getArgument(0)).run();
+                                return null;
+                            }
+                        });
     }
 
     @Test
     public void toggle_isShowingTrue_instanceShouldBeNull() {
         when(mDialog.isShowing()).thenReturn(true);
 
-        mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
+        KeyboardShortcuts.toggle(mContext, DEVICE_ID);
 
-        assertThat(mKeyboardShortcuts.sInstance).isNull();
+        assertThat(KeyboardShortcuts.sInstance).isNull();
     }
 
     @Test
     public void toggle_isShowingFalse_showKeyboardShortcuts() {
         when(mDialog.isShowing()).thenReturn(false);
 
-        mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
+        KeyboardShortcuts.toggle(mContext, DEVICE_ID);
 
         verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
         verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
@@ -95,7 +110,7 @@
     public void sanitiseShortcuts_clearsIcons() {
         KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
 
-        KeyboardShortcuts.sanitiseShortcuts(Collections.singletonList(group));
+        KeyboardShortcuts.sanitiseShortcuts(singletonList(group));
 
         verify(group.getItems().get(0)).clearIcon();
         verify(group.getItems().get(1)).clearIcon();
@@ -106,7 +121,7 @@
         KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
         group.setPackageName(null);
 
-        KeyboardShortcuts.sanitiseShortcuts(Collections.singletonList(group));
+        KeyboardShortcuts.sanitiseShortcuts(singletonList(group));
 
         verify(group.getItems().get(0)).clearIcon();
         verify(group.getItems().get(1)).clearIcon();
@@ -116,16 +131,9 @@
     @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
     public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
         KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+        KeyboardShortcuts.toggle(mContext, DEVICE_ID);
 
-        mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
-
-        ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
-                ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
-        ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
-        verify(mWindowManager).requestAppKeyboardShortcuts(callbackCaptor.capture(), anyInt());
-        callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
-        verify(mHandler).post(handlerRunnableCaptor.capture());
-        handlerRunnableCaptor.getValue().run();
+        emitAppShortcuts(singletonList(group), DEVICE_ID);
 
         verify(group.getItems().get(0)).clearIcon();
         verify(group.getItems().get(1)).clearIcon();
@@ -135,20 +143,38 @@
     @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
     public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
         KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+        KeyboardShortcuts.toggle(mContext, DEVICE_ID);
 
-        mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
-
-        ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
-                ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
-        ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
-        verify(mWindowManager).requestImeKeyboardShortcuts(callbackCaptor.capture(), anyInt());
-        callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
-        verify(mHandler).post(handlerRunnableCaptor.capture());
-        handlerRunnableCaptor.getValue().run();
+        emitImeShortcuts(singletonList(group), DEVICE_ID);
 
         verify(group.getItems().get(0)).clearIcon();
         verify(group.getItems().get(1)).clearIcon();
+    }
 
+    @Test
+    public void onImeAndAppShortcutsReceived_appShortcutsNull_doesNotCrash() {
+        KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+        KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+        emitImeShortcuts(singletonList(group), DEVICE_ID);
+        emitAppShortcuts(/* groups= */ null, DEVICE_ID);
+    }
+
+    @Test
+    public void onImeAndAppShortcutsReceived_imeShortcutsNull_doesNotCrash() {
+        KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+        KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+        emitAppShortcuts(singletonList(group), DEVICE_ID);
+        emitImeShortcuts(/* groups= */ null, DEVICE_ID);
+    }
+
+    @Test
+    public void onImeAndAppShortcutsReceived_bothNull_doesNotCrash() {
+        KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+        emitImeShortcuts(/* groups= */ null, DEVICE_ID);
+        emitAppShortcuts(/* groups= */ null, DEVICE_ID);
     }
 
     private KeyboardShortcutGroup createKeyboardShortcutGroupForIconTests() {
@@ -159,9 +185,23 @@
         when(info1.getIcon()).thenReturn(icon);
         when(info2.getIcon()).thenReturn(icon);
 
-        KeyboardShortcutGroup group = new KeyboardShortcutGroup("label",
-                Arrays.asList(new KeyboardShortcutInfo[]{ info1, info2}));
+        KeyboardShortcutGroup group =
+                new KeyboardShortcutGroup("label", Arrays.asList(info1, info2));
         group.setPackageName("com.example");
         return group;
     }
+
+    private void emitImeShortcuts(@Nullable List<KeyboardShortcutGroup> groups, int deviceId) {
+        ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+                ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+        verify(mWindowManager).requestImeKeyboardShortcuts(callbackCaptor.capture(), eq(deviceId));
+        callbackCaptor.getValue().onKeyboardShortcutsReceived(groups);
+    }
+
+    private void emitAppShortcuts(@Nullable List<KeyboardShortcutGroup> groups, int deviceId) {
+        ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+                ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+        verify(mWindowManager).requestAppKeyboardShortcuts(callbackCaptor.capture(), eq(deviceId));
+        callbackCaptor.getValue().onKeyboardShortcutsReceived(groups);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index b6ee46d..50131cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -97,7 +97,8 @@
 
         mIconView = new StatusBarIconView(mContext, "test_slot", null);
         mStatusBarIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
-                Icon.createWithResource(mContext, R.drawable.ic_android), 0, 0, "");
+                Icon.createWithResource(mContext, R.drawable.ic_android), 0, 0, "",
+                StatusBarIcon.Type.SystemIcon);
     }
 
     @Test
@@ -138,7 +139,7 @@
         Bitmap largeBitmap = Bitmap.createBitmap(6000, 6000, Bitmap.Config.ARGB_8888);
         Icon icon = Icon.createWithBitmap(largeBitmap);
         StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
-                icon, 0, 0, "");
+                icon, 0, 0, "", StatusBarIcon.Type.SystemIcon);
         assertTrue(mIconView.set(largeIcon));
 
         // The view should downscale the bitmap.
@@ -152,7 +153,7 @@
         Bitmap bitmap = Bitmap.createBitmap(60, 60, Bitmap.Config.ARGB_8888);
         Icon icon = Icon.createWithBitmap(bitmap);
         StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
-                icon, 0, 0, "");
+                icon, 0, 0, "", StatusBarIcon.Type.SystemIcon);
         mIconView.setNotification(getMockSbn());
         mIconView.getIcon(largeIcon);
         // no crash? good
@@ -172,7 +173,7 @@
         Bitmap bitmap = Bitmap.createBitmap(60, 60, Bitmap.Config.ARGB_8888);
         Icon icon = Icon.createWithBitmap(bitmap);
         StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
-                icon, 0, 0, "");
+                icon, 0, 0, "", StatusBarIcon.Type.SystemIcon);
         mIconView.getIcon(largeIcon);
         // No crash? good
     }
@@ -430,7 +431,7 @@
                 width, height, Bitmap.Config.ARGB_8888);
         Icon icon = Icon.createWithBitmap(bitmap);
         mStatusBarIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
-                icon, 0, 0, "");
+                icon, 0, 0, "", StatusBarIcon.Type.SystemIcon);
         // Since we only want to verify icon scale logic here, we directly use
         // {@link StatusBarIconView#setImageDrawable(Drawable)} to set the image drawable
         // to iconView instead of call {@link StatusBarIconView#set(StatusBarIcon)}. It's to prevent
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
new file mode 100644
index 0000000..cd8a740
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.chips.call.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+
+@SmallTest
+class CallChipInteractorTest : SysuiTestCase() {
+    val kosmos = Kosmos()
+    val repo = kosmos.ongoingCallRepository
+
+    val underTest = kosmos.callChipInteractor
+
+    @Test
+    fun ongoingCallState_matchesRepo() =
+        kosmos.testScope.runTest {
+            val latest by collectLastValue(underTest.ongoingCallState)
+
+            val inCall = OngoingCallModel.InCall(startTimeMs = 1000, intent = null)
+            repo.setOngoingCallState(inCall)
+            assertThat(latest).isEqualTo(inCall)
+
+            val noCall = OngoingCallModel.NoCall
+            repo.setOngoingCallState(noCall)
+            assertThat(latest).isEqualTo(noCall)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
new file mode 100644
index 0000000..6cac30a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.call.ui.viewmodel
+
+import android.app.PendingIntent
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class CallChipViewModelTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val repo = kosmos.ongoingCallRepository
+
+    private val chipBackgroundView = mock<ChipBackgroundContainer>()
+    private val chipView =
+        mock<View>().apply {
+            whenever(
+                    this.requireViewById<ChipBackgroundContainer>(
+                        R.id.ongoing_activity_chip_background
+                    )
+                )
+                .thenReturn(chipBackgroundView)
+        }
+
+    private val underTest = kosmos.callChipViewModel
+
+    @Test
+    fun chip_noCall_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(OngoingCallModel.NoCall)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_inCall_isShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 345, intent = null))
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+        }
+
+    @Test
+    fun chip_inCall_startTimeConvertedToElapsedRealtime() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            kosmos.fakeSystemClock.setCurrentTimeMillis(3000)
+            kosmos.fakeSystemClock.setElapsedRealtime(400_000)
+
+            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+
+            // The OngoingCallModel start time is relative to currentTimeMillis, so this call
+            // started 2000ms ago (1000 - 3000). The OngoingActivityChipModel start time needs to be
+            // relative to elapsedRealtime, so it should be 2000ms before the elapsed realtime set
+            // on the clock.
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(398_000)
+        }
+
+    @Test
+    fun chip_inCall_iconIsPhone() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+
+            assertThat(((latest as OngoingActivityChipModel.Shown).icon as Icon.Resource).res)
+                .isEqualTo(com.android.internal.R.drawable.ic_phone)
+        }
+
+    @Test
+    fun chip_resetsCorrectly() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            kosmos.fakeSystemClock.setCurrentTimeMillis(3000)
+            kosmos.fakeSystemClock.setElapsedRealtime(400_000)
+
+            // Start a call
+            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(398_000)
+
+            // End the call
+            repo.setOngoingCallState(OngoingCallModel.NoCall)
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+
+            // Let 100_000ms elapse
+            kosmos.fakeSystemClock.setCurrentTimeMillis(103_000)
+            kosmos.fakeSystemClock.setElapsedRealtime(500_000)
+
+            // Start a new call, which started 1000ms ago
+            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 102_000, intent = null))
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(499_000)
+        }
+
+    @Test
+    fun chip_inCall_nullIntent_clickListenerDoesNothing() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = null))
+
+            val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
+
+            clickListener.onClick(chipView)
+            // Just verify nothing crashes
+        }
+
+    @Test
+    fun chip_inCall_validIntent_clickListenerLaunchesIntent() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            val intent = mock<PendingIntent>()
+            repo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 1000, intent = intent))
+            val clickListener = (latest as OngoingActivityChipModel.Shown).onClickListener
+
+            clickListener.onClick(chipView)
+
+            verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(intent, null)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt
new file mode 100644
index 0000000..c6fb481
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.casttootherdevice.ui.view
+
+import android.content.ComponentName
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
+import com.android.systemui.statusbar.phone.SystemUIDialog
+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.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() {
+    private val kosmos = Kosmos().also { it.testCase = this }
+    private val sysuiDialog = mock<SystemUIDialog>()
+    private lateinit var underTest: EndCastToOtherDeviceDialogDelegate
+
+    @Test
+    fun icon() {
+        createAndSetDelegate(ENTIRE_SCREEN)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setIcon(R.drawable.ic_cast_connected)
+    }
+
+    @Test
+    fun title() {
+        createAndSetDelegate(SINGLE_TASK)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setTitle(R.string.cast_to_other_device_stop_dialog_title)
+    }
+
+    @Test
+    fun message_entireScreen() {
+        createAndSetDelegate(ENTIRE_SCREEN)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog)
+            .setMessage(context.getString(R.string.cast_to_other_device_stop_dialog_message))
+    }
+
+    @Test
+    fun message_singleTask() {
+        val baseIntent =
+            Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+        val appInfo = mock<ApplicationInfo>()
+        whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+        whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+            .thenReturn(appInfo)
+
+        createAndSetDelegate(
+            MediaProjectionState.Projecting.SingleTask(
+                HOST_PACKAGE,
+                createTask(taskId = 1, baseIntent = baseIntent)
+            )
+        )
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        // It'd be nice to use R.string.cast_to_other_device_stop_dialog_message_specific_app
+        // directly, but it includes the <b> tags which aren't in the returned string.
+        val result = argumentCaptor<CharSequence>()
+        verify(sysuiDialog).setMessage(result.capture())
+        assertThat(result.firstValue.toString()).isEqualTo("You will stop casting Fake Package")
+    }
+
+    @Test
+    fun negativeButton() {
+        createAndSetDelegate(ENTIRE_SCREEN)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null)
+    }
+
+    @Test
+    fun positiveButton() =
+        kosmos.testScope.runTest {
+            createAndSetDelegate(SINGLE_TASK)
+
+            underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+            val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
+
+            // Verify the button has the right text
+            verify(sysuiDialog)
+                .setPositiveButton(
+                    eq(R.string.cast_to_other_device_stop_dialog_button),
+                    clickListener.capture()
+                )
+
+            // Verify that clicking the button stops the recording
+            assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse()
+
+            clickListener.firstValue.onClick(mock<DialogInterface>(), 0)
+            runCurrent()
+
+            assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue()
+        }
+
+    private fun createAndSetDelegate(state: MediaProjectionState.Projecting) {
+        underTest =
+            EndCastToOtherDeviceDialogDelegate(
+                kosmos.endMediaProjectionDialogHelper,
+                stopAction = kosmos.mediaProjectionChipInteractor::stopProjecting,
+                ProjectionChipModel.Projecting(
+                    ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE,
+                    state,
+                ),
+            )
+    }
+
+    companion object {
+        private const val HOST_PACKAGE = "fake.host.package"
+        private val ENTIRE_SCREEN = MediaProjectionState.Projecting.EntireScreen(HOST_PACKAGE)
+        private val SINGLE_TASK =
+            MediaProjectionState.Projecting.SingleTask(HOST_PACKAGE, createTask(taskId = 1))
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
new file mode 100644
index 0000000..93d781f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -0,0 +1,210 @@
+/*
+ * 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.chips.casttootherdevice.ui.viewmodel
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.casttootherdevice.ui.view.EndCastToOtherDeviceDialogDelegate
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.CAST_TO_OTHER_DEVICES_PACKAGE
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.ArgumentMatchers
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
+    private val kosmos = Kosmos().also { it.testCase = this }
+    private val testScope = kosmos.testScope
+    private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
+    private val systemClock = kosmos.fakeSystemClock
+
+    private val mockCastDialog = mock<SystemUIDialog>()
+
+    private val chipBackgroundView = mock<ChipBackgroundContainer>()
+    private val chipView =
+        mock<View>().apply {
+            whenever(
+                    this.requireViewById<ChipBackgroundContainer>(
+                        R.id.ongoing_activity_chip_background
+                    )
+                )
+                .thenReturn(chipBackgroundView)
+        }
+
+    private val underTest = kosmos.castToOtherDeviceChipViewModel
+
+    @Before
+    fun setUp() {
+        setUpPackageManagerForMediaProjection(kosmos)
+
+        whenever(kosmos.mockSystemUIDialogFactory.create(any<EndCastToOtherDeviceDialogDelegate>()))
+            .thenReturn(mockCastDialog)
+    }
+
+    @Test
+    fun chip_notProjectingState_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_singleTaskState_otherDevicesPackage_isShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    CAST_TO_OTHER_DEVICES_PACKAGE,
+                    createTask(taskId = 1),
+                )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+        }
+
+    @Test
+    fun chip_entireScreenState_otherDevicesPackage_isShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+        }
+
+    @Test
+    fun chip_singleTaskState_normalPackage_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(NORMAL_PACKAGE, createTask(taskId = 1))
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_entireScreenState_normalPackage_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_timeResetsOnEachNewShare() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            systemClock.setElapsedRealtime(1234)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+
+            systemClock.setElapsedRealtime(5678)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    CAST_TO_OTHER_DEVICES_PACKAGE,
+                    createTask(taskId = 1),
+                )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+        }
+
+    @Test
+    fun chip_entireScreen_clickListenerShowsCastDialog() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+            val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+            clickListener.onClick(chipView)
+            verify(kosmos.mockDialogTransitionAnimator)
+                .showFromView(
+                    eq(mockCastDialog),
+                    eq(chipBackgroundView),
+                    eq(null),
+                    ArgumentMatchers.anyBoolean(),
+                )
+        }
+
+    @Test
+    fun chip_singleTask_clickListenerShowsCastDialog() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    CAST_TO_OTHER_DEVICES_PACKAGE,
+                    createTask(taskId = 1),
+                )
+
+            val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+            clickListener.onClick(chipView)
+            verify(kosmos.mockDialogTransitionAnimator)
+                .showFromView(
+                    eq(mockCastDialog),
+                    eq(chipBackgroundView),
+                    eq(null),
+                    ArgumentMatchers.anyBoolean(),
+                )
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
index 0f33b9d..7eb4ca0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -16,85 +16,150 @@
 
 package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
 
+import android.Manifest
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.ui.viewmodel.mediaProjectionChipInteractor
-import com.android.systemui.util.time.fakeSystemClock
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.doAnswer
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @SmallTest
 class MediaProjectionChipInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
     private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
-    private val systemClock = kosmos.fakeSystemClock
+
+    @Before
+    fun setUp() {
+        setUpPackageManagerForMediaProjection(kosmos)
+    }
 
     private val underTest = kosmos.mediaProjectionChipInteractor
 
     @Test
-    fun chip_notProjectingState_isHidden() =
+    fun projection_notProjectingState_isNotProjecting() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.projection)
 
             mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+            assertThat(latest).isInstanceOf(ProjectionChipModel.NotProjecting::class.java)
         }
 
     @Test
-    fun chip_singleTaskState_isShownWithIcon() =
+    fun projection_singleTaskState_otherDevicesPackage_isCastToOtherDeviceType() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.projection)
 
             mediaProjectionRepo.mediaProjectionState.value =
-                MediaProjectionState.SingleTask(createTask(taskId = 1))
+                MediaProjectionState.Projecting.SingleTask(
+                    CAST_TO_OTHER_DEVICES_PACKAGE,
+                    createTask(taskId = 1)
+                )
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+            assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
+            assertThat((latest as ProjectionChipModel.Projecting).type)
+                .isEqualTo(ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE)
         }
 
     @Test
-    fun chip_entireScreenState_isShownWithIcon() =
+    fun projection_entireScreenState_otherDevicesPackage_isCastToOtherDeviceChipType() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.projection)
 
-            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(
+                    CAST_TO_OTHER_DEVICES_PACKAGE,
+                )
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+            assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
+            assertThat((latest as ProjectionChipModel.Projecting).type)
+                .isEqualTo(ProjectionChipModel.Type.CAST_TO_OTHER_DEVICE)
         }
 
     @Test
-    fun chip_timeResetsOnEachNewShare() =
+    fun projection_singleTaskState_normalPackage_isShareToAppChipType() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.projection)
 
-            systemClock.setElapsedRealtime(1234)
-            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(NORMAL_PACKAGE, createTask(taskId = 1))
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
-
-            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
-
-            systemClock.setElapsedRealtime(5678)
-            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
-
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+            assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
+            assertThat((latest as ProjectionChipModel.Projecting).type)
+                .isEqualTo(ProjectionChipModel.Type.SHARE_TO_APP)
         }
+
+    @Test
+    fun projection_entireScreenState_normalPackage_isShareToAppChipType() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.projection)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            assertThat(latest).isInstanceOf(ProjectionChipModel.Projecting::class.java)
+            assertThat((latest as ProjectionChipModel.Projecting).type)
+                .isEqualTo(ProjectionChipModel.Type.SHARE_TO_APP)
+        }
+
+    companion object {
+        const val CAST_TO_OTHER_DEVICES_PACKAGE = "other.devices.package"
+        const val NORMAL_PACKAGE = "some.normal.package"
+
+        /**
+         * Sets up [kosmos.packageManager] so that [CAST_TO_OTHER_DEVICES_PACKAGE] is marked as a
+         * package that casts to other devices, and [NORMAL_PACKAGE] is *not* marked as casting to
+         * other devices.
+         */
+        fun setUpPackageManagerForMediaProjection(kosmos: Kosmos) {
+            kosmos.packageManager.apply {
+                whenever(
+                        this.checkPermission(
+                            Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+                            CAST_TO_OTHER_DEVICES_PACKAGE
+                        )
+                    )
+                    .thenReturn(PackageManager.PERMISSION_GRANTED)
+                whenever(
+                        this.checkPermission(
+                            Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+                            NORMAL_PACKAGE
+                        )
+                    )
+                    .thenReturn(PackageManager.PERMISSION_DENIED)
+
+                doAnswer {
+                        // See Utils.isHeadlessRemoteDisplayProvider
+                        if (
+                            (it.arguments[0] as Intent).`package` == CAST_TO_OTHER_DEVICES_PACKAGE
+                        ) {
+                            emptyList()
+                        } else {
+                            listOf(mock<ResolveInfo>())
+                        }
+                    }
+                    .whenever(this)
+                    .queryIntentActivities(any(), anyInt())
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.kt
new file mode 100644
index 0000000..ee929ae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperTest.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.statusbar.chips.mediaprojection.ui.view
+
+import android.content.ComponentName
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class EndMediaProjectionDialogHelperTest : SysuiTestCase() {
+    private val kosmos = Kosmos().also { it.testCase = this }
+
+    private val underTest = kosmos.endMediaProjectionDialogHelper
+
+    @Test
+    fun createDialog_usesDelegateAndFactory() {
+        val dialog = mock<SystemUIDialog>()
+        val delegate = SystemUIDialog.Delegate { dialog }
+        whenever(kosmos.mockSystemUIDialogFactory.create(eq(delegate))).thenReturn(dialog)
+
+        underTest.createDialog(delegate)
+
+        verify(kosmos.mockSystemUIDialogFactory).create(delegate)
+    }
+
+    @Test
+    fun getDialogMessage_entireScreen_isGenericMessage() {
+        val result =
+            underTest.getDialogMessage(
+                MediaProjectionState.Projecting.EntireScreen("host.package"),
+                R.string.accessibility_home,
+                R.string.cast_to_other_device_stop_dialog_message_specific_app
+            )
+
+        assertThat(result).isEqualTo(context.getString(R.string.accessibility_home))
+    }
+
+    @Test
+    fun getDialogMessage_singleTask_cannotFindPackage_isGenericMessage() {
+        val baseIntent =
+            Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+        whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+            .thenThrow(PackageManager.NameNotFoundException())
+
+        val projectionState =
+            MediaProjectionState.Projecting.SingleTask(
+                "host.package",
+                createTask(taskId = 1, baseIntent = baseIntent)
+            )
+
+        val result =
+            underTest.getDialogMessage(
+                projectionState,
+                R.string.accessibility_home,
+                R.string.cast_to_other_device_stop_dialog_message_specific_app
+            )
+
+        assertThat(result).isEqualTo(context.getString(R.string.accessibility_home))
+    }
+
+    @Test
+    fun getDialogMessage_singleTask_findsPackage_isSpecificMessageWithAppLabel() {
+        val baseIntent =
+            Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+        val appInfo = mock<ApplicationInfo>()
+        whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+        whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+            .thenReturn(appInfo)
+
+        val projectionState =
+            MediaProjectionState.Projecting.SingleTask(
+                "host.package",
+                createTask(taskId = 1, baseIntent = baseIntent)
+            )
+
+        val result =
+            underTest.getDialogMessage(
+                projectionState,
+                R.string.accessibility_home,
+                R.string.cast_to_other_device_stop_dialog_message_specific_app
+            )
+
+        // It'd be nice to use the R.string resources directly, but they include the <b> tags which
+        // aren't in the returned string.
+        assertThat(result.toString()).isEqualTo("You will stop casting Fake Package")
+    }
+
+    @Test
+    fun getDialogMessage_nullTask_isGenericMessage() {
+        val result =
+            underTest.getDialogMessage(
+                specificTaskInfo = null,
+                R.string.accessibility_home,
+                R.string.cast_to_other_device_stop_dialog_message_specific_app,
+            )
+
+        assertThat(result).isEqualTo(context.getString(R.string.accessibility_home))
+    }
+
+    @Test
+    fun getDialogMessage_withTask_cannotFindPackage_isGenericMessage() {
+        val baseIntent =
+            Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+        whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+            .thenThrow(PackageManager.NameNotFoundException())
+        val task = createTask(taskId = 1, baseIntent = baseIntent)
+
+        val result =
+            underTest.getDialogMessage(
+                task,
+                R.string.accessibility_home,
+                R.string.cast_to_other_device_stop_dialog_message_specific_app
+            )
+
+        assertThat(result).isEqualTo(context.getString(R.string.accessibility_home))
+    }
+
+    @Test
+    fun getDialogMessage_withTask_findsPackage_isSpecificMessageWithAppLabel() {
+        val baseIntent =
+            Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+        val appInfo = mock<ApplicationInfo>()
+        whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+        whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+            .thenReturn(appInfo)
+        val task = createTask(taskId = 1, baseIntent = baseIntent)
+
+        val result =
+            underTest.getDialogMessage(
+                task,
+                R.string.accessibility_home,
+                R.string.cast_to_other_device_stop_dialog_message_specific_app
+            )
+
+        assertThat(result.toString()).isEqualTo("You will stop casting Fake Package")
+    }
+
+    @Test
+    fun getDialogMessage_appLabelHasSpecialCharacters_isEscaped() {
+        val baseIntent =
+            Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+        val appInfo = mock<ApplicationInfo>()
+        whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake & Package <Here>")
+        whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+            .thenReturn(appInfo)
+
+        val projectionState =
+            MediaProjectionState.Projecting.SingleTask(
+                "host.package",
+                createTask(taskId = 1, baseIntent = baseIntent)
+            )
+
+        val result =
+            underTest.getDialogMessage(
+                projectionState,
+                R.string.accessibility_home,
+                R.string.cast_to_other_device_stop_dialog_message_specific_app
+            )
+
+        assertThat(result.toString()).isEqualTo("You will stop casting Fake & Package <Here>")
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
index 25efaf1..83b31c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
@@ -18,79 +18,104 @@
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.res.R
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor
-import com.android.systemui.util.time.fakeSystemClock
+import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 
 @SmallTest
 class ScreenRecordChipInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
     private val screenRecordRepo = kosmos.screenRecordRepository
-    private val systemClock = kosmos.fakeSystemClock
+    private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
 
     private val underTest = kosmos.screenRecordChipInteractor
 
     @Test
-    fun chip_doingNothingState_isHidden() =
+    fun screenRecordState_doingNothingState_matches() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.screenRecordState)
 
             screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+            assertThat(latest).isInstanceOf(ScreenRecordChipModel.DoingNothing::class.java)
         }
 
     @Test
-    fun chip_startingState_isHidden() =
+    fun screenRecordState_startingState_matches() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.screenRecordState)
 
             screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(400)
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+            assertThat(latest).isEqualTo(ScreenRecordChipModel.Starting(400))
         }
 
     @Test
-    fun chip_recordingState_isShownWithIcon() =
+    fun screenRecordState_recordingState_matches() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.screenRecordState)
 
             screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+            assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java)
         }
 
     @Test
-    fun chip_timeResetsOnEachNewRecording() =
+    fun screenRecordState_projectionIsNotProjecting_recordedTaskNull() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.screenRecordState)
 
-            systemClock.setElapsedRealtime(1234)
             screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
+        }
 
-            screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+    @Test
+    fun screenRecordState_projectionIsEntireScreen_recordedTaskNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.screenRecordState)
 
-            systemClock.setElapsedRealtime(5678)
             screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen("host.package")
 
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
+        }
+
+    @Test
+    fun screenRecordState_projectionIsSingleTask_recordedTaskMatches() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.screenRecordState)
+
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+            val task = createTask(taskId = 1)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask("host.package", task)
+
+            assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
+        }
+
+    @Test
+    fun stopRecording_sendsToRepo() =
+        testScope.runTest {
+            assertThat(screenRecordRepo.stopRecordingInvoked).isFalse()
+
+            underTest.stopRecording()
+            runCurrent()
+
+            assertThat(screenRecordRepo.stopRecordingInvoked).isTrue()
         }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
new file mode 100644
index 0000000..7e667de
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt
@@ -0,0 +1,147 @@
+/*
+ * 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.chips.screenrecord.ui.view
+
+import android.app.ActivityManager
+import android.content.ComponentName
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor
+import com.android.systemui.statusbar.phone.SystemUIDialog
+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.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class EndScreenRecordingDialogDelegateTest : SysuiTestCase() {
+    private val kosmos = Kosmos().also { it.testCase = this }
+
+    private val sysuiDialog = mock<SystemUIDialog>()
+
+    private lateinit var underTest: EndScreenRecordingDialogDelegate
+
+    @Test
+    fun icon() {
+        createAndSetDelegate(recordedTask = null)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setIcon(R.drawable.ic_screenrecord)
+    }
+
+    @Test
+    fun title() {
+        createAndSetDelegate(recordedTask = null)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setTitle(R.string.screenrecord_stop_dialog_title)
+    }
+
+    @Test
+    fun message_noRecordedTask() {
+        createAndSetDelegate(recordedTask = null)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setMessage(context.getString(R.string.screenrecord_stop_dialog_message))
+    }
+
+    @Test
+    fun message_hasRecordedTask() {
+        val baseIntent =
+            Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+        val appInfo = mock<ApplicationInfo>()
+        whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+        whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+            .thenReturn(appInfo)
+        val task = createTask(taskId = 1, baseIntent = baseIntent)
+
+        createAndSetDelegate(task)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        // It'd be nice to use R.string.screenrecord_stop_dialog_message_specific_app directly, but
+        // it includes the <b> tags which aren't in the returned string.
+        val result = argumentCaptor<CharSequence>()
+        verify(sysuiDialog).setMessage(result.capture())
+        assertThat(result.firstValue.toString()).isEqualTo("You will stop recording Fake Package")
+    }
+
+    @Test
+    fun negativeButton() {
+        createAndSetDelegate(recordedTask = createTask(taskId = 1))
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null)
+    }
+
+    @Test
+    fun positiveButton() =
+        kosmos.testScope.runTest {
+            createAndSetDelegate(recordedTask = null)
+
+            underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+            val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
+
+            // Verify the button has the right text
+            verify(sysuiDialog)
+                .setPositiveButton(
+                    eq(R.string.screenrecord_stop_dialog_button),
+                    clickListener.capture()
+                )
+
+            // Verify that clicking the button stops the recording
+            assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isFalse()
+
+            clickListener.firstValue.onClick(mock<DialogInterface>(), 0)
+            runCurrent()
+
+            assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isTrue()
+        }
+
+    private fun createAndSetDelegate(recordedTask: ActivityManager.RunningTaskInfo?) {
+        underTest =
+            EndScreenRecordingDialogDelegate(
+                kosmos.endMediaProjectionDialogHelper,
+                stopAction = kosmos.screenRecordChipInteractor::stopRecording,
+                recordedTask,
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
new file mode 100644
index 0000000..45044f7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -0,0 +1,196 @@
+/*
+ * 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.chips.screenrecord.ui.viewmodel
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager
+import com.android.systemui.res.R
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.screenrecord.ui.view.EndScreenRecordingDialogDelegate
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.ArgumentMatchers
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class ScreenRecordChipViewModelTest : SysuiTestCase() {
+    private val kosmos = Kosmos().also { it.testCase = this }
+    private val testScope = kosmos.testScope
+    private val screenRecordRepo = kosmos.screenRecordRepository
+    private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
+    private val systemClock = kosmos.fakeSystemClock
+    private val mockSystemUIDialog = mock<SystemUIDialog>()
+
+    private val chipBackgroundView = mock<ChipBackgroundContainer>()
+    private val chipView =
+        mock<View>().apply {
+            whenever(
+                    this.requireViewById<ChipBackgroundContainer>(
+                        R.id.ongoing_activity_chip_background
+                    )
+                )
+                .thenReturn(chipBackgroundView)
+        }
+
+    private val underTest = kosmos.screenRecordChipViewModel
+
+    @Before
+    fun setUp() {
+        whenever(kosmos.mockSystemUIDialogFactory.create(any<EndScreenRecordingDialogDelegate>()))
+            .thenReturn(mockSystemUIDialog)
+    }
+
+    @Test
+    fun chip_doingNothingState_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_startingState_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(400)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_recordingState_isShownWithIcon() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord)
+        }
+
+    @Test
+    fun chip_timeResetsOnEachNewRecording() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            systemClock.setElapsedRealtime(1234)
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.DoingNothing
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+
+            systemClock.setElapsedRealtime(5678)
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+        }
+
+    @Test
+    fun chip_notProjecting_clickListenerShowsDialog() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+            val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+            clickListener.onClick(chipView)
+            // EndScreenRecordingDialogDelegate will test that the dialog has the right message
+            verify(kosmos.mockDialogTransitionAnimator)
+                .showFromView(
+                    eq(mockSystemUIDialog),
+                    eq(chipBackgroundView),
+                    eq(null),
+                    ArgumentMatchers.anyBoolean(),
+                )
+        }
+
+    @Test
+    fun chip_projectingEntireScreen_clickListenerShowsDialog() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen("host.package")
+
+            val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+            clickListener.onClick(chipView)
+            // EndScreenRecordingDialogDelegate will test that the dialog has the right message
+            verify(kosmos.mockDialogTransitionAnimator)
+                .showFromView(
+                    eq(mockSystemUIDialog),
+                    eq(chipBackgroundView),
+                    eq(null),
+                    ArgumentMatchers.anyBoolean(),
+                )
+        }
+
+    @Test
+    fun chip_projectingSingleTask_clickListenerShowsDialog() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    "host.package",
+                    FakeActivityTaskManager.createTask(taskId = 1)
+                )
+
+            val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+            clickListener.onClick(chipView)
+            // EndScreenRecordingDialogDelegate will test that the dialog has the right message
+            verify(kosmos.mockDialogTransitionAnimator)
+                .showFromView(
+                    eq(mockSystemUIDialog),
+                    eq(chipBackgroundView),
+                    eq(null),
+                    ArgumentMatchers.anyBoolean(),
+                )
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
new file mode 100644
index 0000000..4c2546e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/view/EndShareToAppDialogDelegateTest.kt
@@ -0,0 +1,157 @@
+/*
+ * 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.chips.sharetoapp.ui.view
+
+import android.content.ComponentName
+import android.content.DialogInterface
+import android.content.Intent
+import android.content.packageManager
+import android.content.pm.ApplicationInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
+import com.android.systemui.statusbar.phone.SystemUIDialog
+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.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class EndShareToAppDialogDelegateTest : SysuiTestCase() {
+    private val kosmos = Kosmos().also { it.testCase = this }
+    private val sysuiDialog = mock<SystemUIDialog>()
+    private lateinit var underTest: EndShareToAppDialogDelegate
+
+    @Test
+    fun icon() {
+        createAndSetDelegate(SINGLE_TASK)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setIcon(R.drawable.ic_screenshot_share)
+    }
+
+    @Test
+    fun title() {
+        createAndSetDelegate(ENTIRE_SCREEN)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setTitle(R.string.share_to_app_stop_dialog_title)
+    }
+
+    @Test
+    fun message_entireScreen() {
+        createAndSetDelegate(ENTIRE_SCREEN)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setMessage(context.getString(R.string.share_to_app_stop_dialog_message))
+    }
+
+    @Test
+    fun message_singleTask() {
+        val baseIntent =
+            Intent().apply { this.component = ComponentName("fake.task.package", "cls") }
+        val appInfo = mock<ApplicationInfo>()
+        whenever(appInfo.loadLabel(kosmos.packageManager)).thenReturn("Fake Package")
+        whenever(kosmos.packageManager.getApplicationInfo(eq("fake.task.package"), any<Int>()))
+            .thenReturn(appInfo)
+
+        createAndSetDelegate(
+            MediaProjectionState.Projecting.SingleTask(
+                HOST_PACKAGE,
+                createTask(taskId = 1, baseIntent = baseIntent)
+            )
+        )
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        // It'd be nice to use R.string.share_to_app_stop_dialog_message_specific_app directly, but
+        // it includes the <b> tags which aren't in the returned string.
+        val result = argumentCaptor<CharSequence>()
+        verify(sysuiDialog).setMessage(result.capture())
+        assertThat(result.firstValue.toString()).isEqualTo("You will stop sharing Fake Package")
+    }
+
+    @Test
+    fun negativeButton() {
+        createAndSetDelegate(SINGLE_TASK)
+
+        underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+        verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null)
+    }
+
+    @Test
+    fun positiveButton() =
+        kosmos.testScope.runTest {
+            createAndSetDelegate(ENTIRE_SCREEN)
+
+            underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null)
+
+            val clickListener = argumentCaptor<DialogInterface.OnClickListener>()
+
+            // Verify the button has the right text
+            verify(sysuiDialog)
+                .setPositiveButton(
+                    eq(R.string.share_to_app_stop_dialog_button),
+                    clickListener.capture()
+                )
+
+            // Verify that clicking the button stops the recording
+            assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse()
+
+            clickListener.firstValue.onClick(mock<DialogInterface>(), 0)
+            runCurrent()
+
+            assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue()
+        }
+
+    private fun createAndSetDelegate(state: MediaProjectionState.Projecting) {
+        underTest =
+            EndShareToAppDialogDelegate(
+                kosmos.endMediaProjectionDialogHelper,
+                stopAction = kosmos.mediaProjectionChipInteractor::stopProjecting,
+                ProjectionChipModel.Projecting(ProjectionChipModel.Type.SHARE_TO_APP, state),
+            )
+    }
+
+    companion object {
+        private const val HOST_PACKAGE = "fake.host.package"
+        private val ENTIRE_SCREEN = MediaProjectionState.Projecting.EntireScreen(HOST_PACKAGE)
+        private val SINGLE_TASK =
+            MediaProjectionState.Projecting.SingleTask(HOST_PACKAGE, createTask(taskId = 1))
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
new file mode 100644
index 0000000..9aef526
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -0,0 +1,212 @@
+/*
+ * 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.chips.sharetoapp.ui.viewmodel
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.CAST_TO_OTHER_DEVICES_PACKAGE
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
+import com.android.systemui.statusbar.chips.sharetoapp.ui.view.EndShareToAppDialogDelegate
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.ArgumentMatchers
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class ShareToAppChipViewModelTest : SysuiTestCase() {
+    private val kosmos = Kosmos().also { it.testCase = this }
+    private val testScope = kosmos.testScope
+    private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
+    private val systemClock = kosmos.fakeSystemClock
+
+    private val mockShareDialog = mock<SystemUIDialog>()
+
+    private val chipBackgroundView = mock<ChipBackgroundContainer>()
+    private val chipView =
+        mock<View>().apply {
+            whenever(
+                    this.requireViewById<ChipBackgroundContainer>(
+                        R.id.ongoing_activity_chip_background
+                    )
+                )
+                .thenReturn(chipBackgroundView)
+        }
+
+    private val underTest = kosmos.shareToAppChipViewModel
+
+    @Before
+    fun setUp() {
+        setUpPackageManagerForMediaProjection(kosmos)
+
+        whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareToAppDialogDelegate>()))
+            .thenReturn(mockShareDialog)
+    }
+
+    @Test
+    fun chip_notProjectingState_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_singleTaskState_otherDevicesPackage_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    CAST_TO_OTHER_DEVICES_PACKAGE,
+                    createTask(taskId = 1),
+                )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_entireScreenState_otherDevicesPackage_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_singleTaskState_normalPackage_isShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    NORMAL_PACKAGE,
+                    createTask(taskId = 1),
+                )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
+        }
+
+    @Test
+    fun chip_entireScreenState_normalPackage_isShown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
+        }
+
+    @Test
+    fun chip_timeResetsOnEachNewShare() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            systemClock.setElapsedRealtime(1234)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+
+            systemClock.setElapsedRealtime(5678)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    NORMAL_PACKAGE,
+                    createTask(taskId = 1),
+                )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+        }
+
+    @Test
+    fun chip_entireScreen_clickListenerShowsShareDialog() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+
+            val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+            clickListener.onClick(chipView)
+            verify(kosmos.mockDialogTransitionAnimator)
+                .showFromView(
+                    eq(mockShareDialog),
+                    eq(chipBackgroundView),
+                    eq(null),
+                    ArgumentMatchers.anyBoolean(),
+                )
+        }
+
+    @Test
+    fun chip_singleTask_clickListenerShowsShareDialog() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    NORMAL_PACKAGE,
+                    createTask(taskId = 1),
+                )
+
+            val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener)
+
+            clickListener.onClick(chipView)
+            verify(kosmos.mockDialogTransitionAnimator)
+                .showFromView(
+                    eq(mockShareDialog),
+                    eq(chipBackgroundView),
+                    eq(null),
+                    ArgumentMatchers.anyBoolean(),
+                )
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
new file mode 100644
index 0000000..c9c7359
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.chips.ui.viewmodel
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import kotlin.test.Test
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class OngoingActivityChipViewModelTest : SysuiTestCase() {
+    private val mockSystemUIDialog = mock<SystemUIDialog>()
+    private val dialogDelegate = SystemUIDialog.Delegate { mockSystemUIDialog }
+    private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
+
+    private val chipBackgroundView = mock<ChipBackgroundContainer>()
+    private val chipView =
+        mock<View>().apply {
+            whenever(
+                    this.requireViewById<ChipBackgroundContainer>(
+                        R.id.ongoing_activity_chip_background
+                    )
+                )
+                .thenReturn(chipBackgroundView)
+        }
+
+    @Test
+    fun createDialogLaunchOnClickListener_showsDialogOnClick() {
+        val clickListener =
+            createDialogLaunchOnClickListener(dialogDelegate, dialogTransitionAnimator)
+
+        // Dialogs must be created on the main thread
+        context.mainExecutor.execute {
+            clickListener.onClick(chipView)
+            verify(dialogTransitionAnimator)
+                .showFromView(
+                    eq(mockSystemUIDialog),
+                    eq(chipBackgroundView),
+                    eq(null),
+                    anyBoolean(),
+                )
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 121229c..a7b1411f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -18,10 +18,10 @@
 
 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.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
@@ -29,29 +29,38 @@
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+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.phone.ongoingcall.data.repository.ongoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 
 @SmallTest
 class OngoingActivityChipsViewModelTest : SysuiTestCase() {
-
-    private val kosmos = Kosmos()
+    private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
 
     private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
     private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
-    private val callState = kosmos.callChipInteractor.chip
+    private val callRepo = kosmos.ongoingCallRepository
 
     private val underTest = kosmos.ongoingActivityChipsViewModel
 
+    @Before
+    fun setUp() {
+        setUpPackageManagerForMediaProjection(kosmos)
+    }
+
     @Test
     fun chip_allHidden_hidden() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.DoingNothing
             mediaProjectionState.value = MediaProjectionState.NotProjecting
-            callState.value = OngoingActivityChipModel.Hidden
+            callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
             val latest by collectLastValue(underTest.chip)
 
@@ -63,7 +72,7 @@
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
             mediaProjectionState.value = MediaProjectionState.NotProjecting
-            callState.value = OngoingActivityChipModel.Hidden
+            callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
             val latest by collectLastValue(underTest.chip)
 
@@ -75,12 +84,7 @@
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
 
-            val callChip =
-                OngoingActivityChipModel.Shown(
-                    Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
-                    startTimeMs = 600L,
-                ) {}
-            callState.value = callChip
+            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
 
             val latest by collectLastValue(underTest.chip)
 
@@ -88,11 +92,12 @@
         }
 
     @Test
-    fun chip_screenRecordShowAndMediaProjectionShow_screenRecordShown() =
+    fun chip_screenRecordShowAndShareToAppShow_screenRecordShown() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
-            mediaProjectionState.value = MediaProjectionState.EntireScreen
-            callState.value = OngoingActivityChipModel.Hidden
+            mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+            callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
             val latest by collectLastValue(underTest.chip)
 
@@ -100,62 +105,53 @@
         }
 
     @Test
-    fun chip_mediaProjectionShowAndCallShow_mediaProjectionShown() =
+    fun chip_shareToAppShowAndCallShow_shareToAppShown() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.DoingNothing
-            mediaProjectionState.value = MediaProjectionState.EntireScreen
-            val callChip =
-                OngoingActivityChipModel.Shown(
-                    Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
-                    startTimeMs = 600L,
-                ) {}
-            callState.value = callChip
+            mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
+            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
 
             val latest by collectLastValue(underTest.chip)
 
-            assertIsMediaProjectionChip(latest)
+            assertIsShareToAppChip(latest)
         }
 
     @Test
-    fun chip_screenRecordAndMediaProjectionHideAndCallShown_callShown() =
+    fun chip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.DoingNothing
+            // MediaProjection covers both share-to-app and cast-to-other-device
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            val callChip =
-                OngoingActivityChipModel.Shown(
-                    Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
-                    startTimeMs = 600L,
-                ) {}
-            callState.value = callChip
+            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
 
             val latest by collectLastValue(underTest.chip)
 
-            assertThat(latest).isEqualTo(callChip)
+            assertIsCallChip(latest)
         }
 
     @Test
     fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
         testScope.runTest {
             // Start with just the lower priority call chip
-            val callChip =
-                OngoingActivityChipModel.Shown(
-                    Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
-                    startTimeMs = 600L,
-                ) {}
-            callState.value = callChip
+            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
             mediaProjectionState.value = MediaProjectionState.NotProjecting
             screenRecordState.value = ScreenRecordModel.DoingNothing
 
             val latest by collectLastValue(underTest.chip)
 
-            assertThat(latest).isEqualTo(callChip)
+            assertIsCallChip(latest)
 
             // WHEN the higher priority media projection chip is added
-            mediaProjectionState.value = MediaProjectionState.SingleTask(createTask(taskId = 1))
+            mediaProjectionState.value =
+                MediaProjectionState.Projecting.SingleTask(
+                    NORMAL_PACKAGE,
+                    createTask(taskId = 1),
+                )
 
             // THEN the higher priority media projection chip is used
-            assertIsMediaProjectionChip(latest)
+            assertIsShareToAppChip(latest)
 
             // WHEN the higher priority screen record chip is added
             screenRecordState.value = ScreenRecordModel.Recording
@@ -169,14 +165,10 @@
         testScope.runTest {
             // WHEN all chips are active
             screenRecordState.value = ScreenRecordModel.Recording
-            mediaProjectionState.value = MediaProjectionState.EntireScreen
+            mediaProjectionState.value =
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
 
-            val callChip =
-                OngoingActivityChipModel.Shown(
-                    Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
-                    startTimeMs = 600L,
-                ) {}
-            callState.value = callChip
+            callRepo.setOngoingCallState(OngoingCallModel.InCall(startTimeMs = 34, intent = null))
 
             val latest by collectLastValue(underTest.chip)
 
@@ -187,26 +179,33 @@
             screenRecordState.value = ScreenRecordModel.DoingNothing
 
             // THEN the lower priority media projection is used
-            assertIsMediaProjectionChip(latest)
+            assertIsShareToAppChip(latest)
 
             // WHEN the higher priority media projection is removed
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
             // THEN the lower priority call is used
-            assertThat(latest).isEqualTo(callChip)
+            assertIsCallChip(latest)
         }
 
     companion object {
         fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord)
         }
 
-        fun assertIsMediaProjectionChip(latest: OngoingActivityChipModel?) {
+        fun assertIsShareToAppChip(latest: OngoingActivityChipModel?) {
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             val icon = (latest as OngoingActivityChipModel.Shown).icon
-            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share)
+        }
+
+        fun assertIsCallChip(latest: OngoingActivityChipModel?) {
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res)
+                .isEqualTo(com.android.internal.R.drawable.ic_phone)
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index 057dcb2..4b250b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
 import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -390,7 +391,9 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            ongoingCallRepository.setHasOngoingCall(true)
+            ongoingCallRepository.setOngoingCallState(
+                OngoingCallModel.InCall(startTimeMs = 34, intent = null)
+            )
             onSystemBarAttributesChanged(
                 requestedVisibleTypes = WindowInsets.Type.navigationBars(),
             )
@@ -403,7 +406,9 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            ongoingCallRepository.setHasOngoingCall(true)
+            ongoingCallRepository.setOngoingCallState(
+                OngoingCallModel.InCall(startTimeMs = 789, intent = null)
+            )
             onSystemBarAttributesChanged(
                 requestedVisibleTypes = WindowInsets.Type.statusBars(),
                 appearance = APPEARANCE_OPAQUE_STATUS_BARS,
@@ -417,7 +422,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.statusBarAppearance)
 
-            ongoingCallRepository.setHasOngoingCall(false)
+            ongoingCallRepository.setOngoingCallState(OngoingCallModel.NoCall)
             onSystemBarAttributesChanged(
                 requestedVisibleTypes = WindowInsets.Type.navigationBars(),
                 appearance = APPEARANCE_OPAQUE_STATUS_BARS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 068e166..b944d72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -136,6 +136,9 @@
     private lateinit var handler: Handler
 
     @Mock
+    private lateinit var bgHandler: Handler
+
+    @Mock
     private lateinit var datePlugin: BcSmartspaceDataPlugin
 
     @Mock
@@ -265,6 +268,7 @@
                 executor,
                 bgExecutor,
                 handler,
+                bgHandler,
                 Optional.of(datePlugin),
                 Optional.of(weatherPlugin),
                 Optional.of(plugin),
@@ -762,6 +766,8 @@
         // THEN the existing session is reused and views are registered
         verify(smartspaceManager, never()).createSmartspaceSession(any())
         verify(smartspaceView2).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+        verify(smartspaceView2).setBgHandler(bgHandler)
+        verify(smartspaceView2).setTimeChangedDelegate(any())
         verify(smartspaceView2).registerDataProvider(plugin)
         verify(smartspaceView2).registerConfigProvider(configPlugin)
     }
@@ -836,6 +842,8 @@
 
         verify(dateSmartspaceView).setUiSurface(
                 BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+        verify(dateSmartspaceView).setTimeChangedDelegate(any())
+        verify(dateSmartspaceView).setBgHandler(bgHandler)
         verify(dateSmartspaceView).registerDataProvider(datePlugin)
 
         verify(dateSmartspaceView).setPrimaryTextColor(anyInt())
@@ -848,6 +856,8 @@
 
         verify(weatherSmartspaceView).setUiSurface(
                 BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+        verify(weatherSmartspaceView).setTimeChangedDelegate(any())
+        verify(weatherSmartspaceView).setBgHandler(bgHandler)
         verify(weatherSmartspaceView).registerDataProvider(weatherPlugin)
 
         verify(weatherSmartspaceView).setPrimaryTextColor(anyInt())
@@ -859,6 +869,8 @@
         controller.stateChangeListener.onViewAttachedToWindow(view)
 
         verify(smartspaceView).setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD)
+        verify(smartspaceView).setTimeChangedDelegate(any())
+        verify(smartspaceView).setBgHandler(bgHandler)
         verify(smartspaceView).registerDataProvider(plugin)
         verify(smartspaceView).registerConfigProvider(configPlugin)
         verify(smartspaceSession)
@@ -984,6 +996,13 @@
             override fun setUiSurface(uiSurface: String) {
             }
 
+            override fun setBgHandler(bgHandler: Handler?) {
+            }
+
+            override fun setTimeChangedDelegate(
+                delegate: BcSmartspaceDataPlugin.TimeChangedDelegate?
+            ) {}
+
             override fun setDozeAmount(amount: Float) {
             }
 
@@ -1012,6 +1031,13 @@
             override fun setUiSurface(uiSurface: String) {
             }
 
+            override fun setBgHandler(bgHandler: Handler?) {
+            }
+
+            override fun setTimeChangedDelegate(
+                delegate: BcSmartspaceDataPlugin.TimeChangedDelegate?
+            ) {}
+
             override fun setDozeAmount(amount: Float) {
             }
 
@@ -1036,6 +1062,13 @@
             override fun setUiSurface(uiSurface: String) {
             }
 
+            override fun setBgHandler(bgHandler: Handler?) {
+            }
+
+            override fun setTimeChangedDelegate(
+                delegate: BcSmartspaceDataPlugin.TimeChangedDelegate?
+            ) {}
+
             override fun setDozeAmount(amount: Float) {
             }
 
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 9e733be..acb005f 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
@@ -19,71 +19,86 @@
 import android.platform.test.annotations.DisableFlags
 import android.provider.DeviceConfig
 import android.provider.Settings
-
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 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.PriorityPeopleSection
 import com.android.systemui.util.DeviceConfigProxyFake
 import com.android.systemui.util.Utils
 import com.android.systemui.util.mockito.any
-
 import org.junit.After
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoSession
+import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
-@DisableFlags(PriorityPeopleSection.FLAG_NAME)  // this class has no logic with the flag enabled
+// this class has no testable logic with either of these flags enabled
+@DisableFlags(PriorityPeopleSection.FLAG_NAME, NotificationMinimalismPrototype.V2.FLAG_NAME)
 class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
-    var manager: NotificationSectionsFeatureManager? = null
-    val proxyFake = DeviceConfigProxyFake()
+    lateinit var manager: NotificationSectionsFeatureManager
+    private val proxyFake = DeviceConfigProxyFake()
     private lateinit var staticMockSession: MockitoSession
 
     @Before
-    public fun setup() {
+    fun setup() {
         manager = NotificationSectionsFeatureManager(proxyFake, mContext)
-        manager!!.clearCache()
-        staticMockSession = ExtendedMockito.mockitoSession()
-            .mockStatic<Utils>(Utils::class.java)
-            .strictness(Strictness.LENIENT)
-            .startMocking()
-        `when`(Utils.useQsMediaPlayer(any())).thenReturn(false)
-        Settings.Global.putInt(context.getContentResolver(),
-                Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 0)
+        manager.clearCache()
+        staticMockSession =
+            ExtendedMockito.mockitoSession()
+                .mockStatic(Utils::class.java)
+                .strictness(Strictness.LENIENT)
+                .startMocking()
+        whenever(Utils.useQsMediaPlayer(any())).thenReturn(false)
+        Settings.Global.putInt(
+            context.getContentResolver(),
+            Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS,
+            0
+        )
     }
 
     @After
-    public fun teardown() {
+    fun teardown() {
         staticMockSession.finishMocking()
     }
 
     @Test
-    public fun testPeopleFilteringOff_newInterruptionModelOn() {
+    fun testPeopleFilteringOff_newInterruptionModelOn() {
         proxyFake.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "false", false)
+            DeviceConfig.NAMESPACE_SYSTEMUI,
+            NOTIFICATIONS_USE_PEOPLE_FILTERING,
+            "false",
+            false
+        )
 
-        assertFalse("People filtering should be disabled", manager!!.isFilteringEnabled())
-        assertTrue("Expecting 2 buckets when people filtering is disabled",
-                manager!!.getNumberOfBuckets() == 2)
+        assertFalse("People filtering should be disabled", manager.isFilteringEnabled())
+        assertTrue(
+            "Expecting 2 buckets when people filtering is disabled",
+            manager.getNumberOfBuckets() == 2
+        )
     }
 
     @Test
-    public fun testPeopleFilteringOn_newInterruptionModelOn() {
+    fun testPeopleFilteringOn_newInterruptionModelOn() {
         proxyFake.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, "true", false)
+            DeviceConfig.NAMESPACE_SYSTEMUI,
+            NOTIFICATIONS_USE_PEOPLE_FILTERING,
+            "true",
+            false
+        )
 
-        assertTrue("People filtering should be enabled", manager!!.isFilteringEnabled())
-        assertTrue("Expecting 5 buckets when people filtering is enabled",
-                manager!!.getNumberOfBuckets() == 5)
+        assertTrue("People filtering should be enabled", manager.isFilteringEnabled())
+        assertTrue(
+            "Expecting 5 buckets when people filtering is enabled",
+            manager.getNumberOfBuckets() == 5
+        )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index c9f2add..26f5370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.notification.shared.byIsSilent
 import com.android.systemui.statusbar.notification.shared.byIsSuppressedFromStatusBar
 import com.android.systemui.statusbar.notification.shared.byKey
+import com.android.systemui.user.domain.UserDomainLayerModule
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -156,7 +157,14 @@
 
     private val bubbles: Bubbles = mock()
 
-    @Component(modules = [SysUITestModule::class, BiometricsDomainLayerModule::class])
+    @Component(
+        modules =
+            [
+                SysUITestModule::class,
+                BiometricsDomainLayerModule::class,
+                UserDomainLayerModule::class,
+            ]
+    )
     @SysUISingleton
     interface TestComponent : SysUITestComponent<AlwaysOnDisplayNotificationIconsInteractor> {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 0d3ab86..b278f1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -41,6 +41,7 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Looper;
+import android.platform.test.annotations.DisableFlags;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.TypedValue;
@@ -60,6 +61,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
 import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
@@ -81,6 +83,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
+@DisableFlags(NotificationRowContentBinderRefactor.FLAG_NAME)
 public class NotificationContentInflaterTest extends SysuiTestCase {
 
     private NotificationContentInflater mNotificationInflater;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
new file mode 100644
index 0000000..e6cba1c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -0,0 +1,572 @@
+/*
+ * 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
+ */
+package com.android.systemui.statusbar.notification.row
+
+import android.app.Notification
+import android.content.Context
+import android.os.AsyncTask
+import android.os.Build
+import android.os.CancellationSignal
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper.RunWithLooper
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import android.widget.RemoteViews
+import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
+import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel
+import com.android.systemui.statusbar.notification.row.shared.NewRemoteViews
+import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel
+import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor
+import com.android.systemui.statusbar.policy.InflatedSmartReplyState
+import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder
+import com.android.systemui.statusbar.policy.SmartReplyStateInflater
+import com.android.systemui.util.concurrency.mockExecutorHandler
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@EnableFlags(NotificationRowContentBinderRefactor.FLAG_NAME)
+class NotificationRowContentBinderImplTest : SysuiTestCase() {
+    private lateinit var mNotificationInflater: NotificationRowContentBinderImpl
+    private lateinit var mBuilder: Notification.Builder
+    private lateinit var mRow: ExpandableNotificationRow
+    private lateinit var mHelper: NotificationTestHelper
+
+    private var mCache: NotifRemoteViewCache = mock()
+    private var mConversationNotificationProcessor: ConversationNotificationProcessor = mock()
+    private var mInflatedSmartReplyState: InflatedSmartReplyState = mock()
+    private var mInflatedSmartReplies: InflatedSmartReplyViewHolder = mock()
+    private var mNotifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider = mock()
+    private var mHeadsUpStyleProvider: HeadsUpStyleProvider = mock()
+    private var mNotifLayoutInflaterFactory: NotifLayoutInflaterFactory = mock()
+    private val mSmartReplyStateInflater: SmartReplyStateInflater =
+        object : SmartReplyStateInflater {
+            override fun inflateSmartReplyViewHolder(
+                sysuiContext: Context,
+                notifPackageContext: Context,
+                entry: NotificationEntry,
+                existingSmartReplyState: InflatedSmartReplyState?,
+                newSmartReplyState: InflatedSmartReplyState
+            ): InflatedSmartReplyViewHolder {
+                return mInflatedSmartReplies
+            }
+
+            override fun inflateSmartReplyState(entry: NotificationEntry): InflatedSmartReplyState {
+                return mInflatedSmartReplyState
+            }
+        }
+
+    @Before
+    fun setUp() {
+        allowTestableLooperAsMainThread()
+        mBuilder =
+            Notification.Builder(mContext, "no-id")
+                .setSmallIcon(R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text")
+                .setStyle(Notification.BigTextStyle().bigText("big text"))
+        mHelper = NotificationTestHelper(mContext, mDependency)
+        val row = mHelper.createRow(mBuilder.build())
+        mRow = spy(row)
+        whenever(mNotifLayoutInflaterFactoryProvider.provide(any(), any()))
+            .thenReturn(mNotifLayoutInflaterFactory)
+        mNotificationInflater =
+            NotificationRowContentBinderImpl(
+                mCache,
+                mock(),
+                mConversationNotificationProcessor,
+                mock(),
+                mSmartReplyStateInflater,
+                mNotifLayoutInflaterFactoryProvider,
+                mHeadsUpStyleProvider,
+                mock()
+            )
+    }
+
+    @Test
+    fun testIncreasedHeadsUpBeingUsed() {
+        val params = BindParams()
+        params.usesIncreasedHeadsUpHeight = true
+        val builder = spy(mBuilder)
+        mNotificationInflater.inflateNotificationViews(
+            mRow.entry,
+            mRow,
+            params,
+            true /* inflateSynchronously */,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+            builder,
+            mContext,
+            mSmartReplyStateInflater
+        )
+        verify(builder).createHeadsUpContentView(true)
+    }
+
+    @Test
+    fun testIncreasedHeightBeingUsed() {
+        val params = BindParams()
+        params.usesIncreasedHeight = true
+        val builder = spy(mBuilder)
+        mNotificationInflater.inflateNotificationViews(
+            mRow.entry,
+            mRow,
+            params,
+            true /* inflateSynchronously */,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+            builder,
+            mContext,
+            mSmartReplyStateInflater
+        )
+        verify(builder).createContentView(true)
+    }
+
+    @Test
+    fun testInflationCallsUpdated() {
+        inflateAndWait(
+            mNotificationInflater,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+            mRow
+        )
+        verify(mRow).onNotificationUpdated()
+    }
+
+    @Test
+    fun testInflationOnlyInflatesSetFlags() {
+        inflateAndWait(
+            mNotificationInflater,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP,
+            mRow
+        )
+        Assert.assertNotNull(mRow.privateLayout.headsUpChild)
+        verify(mRow).onNotificationUpdated()
+    }
+
+    @Test
+    fun testInflationThrowsErrorDoesntCallUpdated() {
+        mRow.privateLayout.removeAllViews()
+        mRow.entry.sbn.notification.contentView =
+            RemoteViews(mContext.packageName, R.layout.status_bar)
+        inflateAndWait(
+            true /* expectingException */,
+            mNotificationInflater,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+            mRow
+        )
+        Assert.assertTrue(mRow.privateLayout.childCount == 0)
+        verify(mRow, times(0)).onNotificationUpdated()
+    }
+
+    @Test
+    fun testAsyncTaskRemoved() {
+        mRow.entry.abortTask()
+        inflateAndWait(
+            mNotificationInflater,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+            mRow
+        )
+        verify(mRow).onNotificationUpdated()
+    }
+
+    @Test
+    fun testRemovedNotInflated() {
+        mRow.setRemoved()
+        mNotificationInflater.setInflateSynchronously(true)
+        mNotificationInflater.bindContent(
+            mRow.entry,
+            mRow,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+            BindParams(),
+            false /* forceInflate */,
+            null /* callback */
+        )
+        Assert.assertNull(mRow.entry.runningTask)
+    }
+
+    @Test
+    @Ignore("b/345418902")
+    fun testInflationIsRetriedIfAsyncFails() {
+        val headsUpStatusBarModel = HeadsUpStatusBarModel("private", "public")
+        val result =
+            NotificationRowContentBinderImpl.InflationProgress(
+                packageContext = mContext,
+                remoteViews = NewRemoteViews(),
+                contentModel = NotificationContentModel(headsUpStatusBarModel)
+            )
+        val countDownLatch = CountDownLatch(1)
+        NotificationRowContentBinderImpl.applyRemoteView(
+            AsyncTask.SERIAL_EXECUTOR,
+            inflateSynchronously = false,
+            isMinimized = false,
+            result = result,
+            reInflateFlags = NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED,
+            inflationId = 0,
+            remoteViewCache = mock(),
+            entry = mRow.entry,
+            row = mRow,
+            isNewView = true, /* isNewView */
+            remoteViewClickHandler = { _, _, _ -> true },
+            callback =
+                object : InflationCallback {
+                    override fun handleInflationException(entry: NotificationEntry, e: Exception) {
+                        countDownLatch.countDown()
+                        throw RuntimeException("No Exception expected")
+                    }
+
+                    override fun onAsyncInflationFinished(entry: NotificationEntry) {
+                        countDownLatch.countDown()
+                    }
+                },
+            parentLayout = mRow.privateLayout,
+            existingView = null,
+            existingWrapper = null,
+            runningInflations = HashMap(),
+            applyCallback =
+                object : NotificationRowContentBinderImpl.ApplyCallback() {
+                    override fun setResultView(v: View) {}
+
+                    override val remoteView: RemoteViews
+                        get() =
+                            AsyncFailRemoteView(
+                                mContext.packageName,
+                                com.android.systemui.tests.R.layout.custom_view_dark
+                            )
+                },
+            logger = mock(),
+        )
+        Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS))
+    }
+
+    @Test
+    fun doesntReapplyDisallowedRemoteView() {
+        mBuilder.setStyle(Notification.MediaStyle())
+        val mediaView = mBuilder.createContentView()
+        mBuilder.setStyle(Notification.DecoratedCustomViewStyle())
+        mBuilder.setCustomContentView(
+            RemoteViews(context.packageName, com.android.systemui.tests.R.layout.custom_view_dark)
+        )
+        val decoratedMediaView = mBuilder.createContentView()
+        Assert.assertFalse(
+            "The decorated media style doesn't allow a view to be reapplied!",
+            NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView)
+        )
+    }
+
+    @Test
+    @Ignore("b/345418902")
+    fun testUsesSameViewWhenCachedPossibleToReuse() {
+        // GIVEN a cached view.
+        val contractedRemoteView = mBuilder.createContentView()
+        whenever(
+                mCache.hasCachedView(
+                    mRow.entry,
+                    NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+                )
+            )
+            .thenReturn(true)
+        whenever(
+                mCache.getCachedView(
+                    mRow.entry,
+                    NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+                )
+            )
+            .thenReturn(contractedRemoteView)
+
+        // GIVEN existing bound view with same layout id.
+        val view = contractedRemoteView.apply(mContext, null /* parent */)
+        mRow.privateLayout.setContractedChild(view)
+
+        // WHEN inflater inflates
+        inflateAndWait(
+            mNotificationInflater,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED,
+            mRow
+        )
+
+        // THEN the view should be re-used
+        Assert.assertEquals(
+            "Binder inflated a new view even though the old one was cached and usable.",
+            view,
+            mRow.privateLayout.contractedChild
+        )
+    }
+
+    @Test
+    fun testInflatesNewViewWhenCachedNotPossibleToReuse() {
+        // GIVEN a cached remote view.
+        val contractedRemoteView = mBuilder.createHeadsUpContentView()
+        whenever(
+                mCache.hasCachedView(
+                    mRow.entry,
+                    NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+                )
+            )
+            .thenReturn(true)
+        whenever(
+                mCache.getCachedView(
+                    mRow.entry,
+                    NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+                )
+            )
+            .thenReturn(contractedRemoteView)
+
+        // GIVEN existing bound view with different layout id.
+        val view: View = TextView(mContext)
+        mRow.privateLayout.setContractedChild(view)
+
+        // WHEN inflater inflates
+        inflateAndWait(
+            mNotificationInflater,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED,
+            mRow
+        )
+
+        // THEN the view should be a new view
+        Assert.assertNotEquals(
+            "Binder (somehow) used the same view when inflating.",
+            view,
+            mRow.privateLayout.contractedChild
+        )
+    }
+
+    @Test
+    fun testInflationCachesCreatedRemoteView() {
+        // WHEN inflater inflates
+        inflateAndWait(
+            mNotificationInflater,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED,
+            mRow
+        )
+
+        // THEN inflater informs cache of the new remote view
+        verify(mCache)
+            .putCachedView(
+                eq(mRow.entry),
+                eq(NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED),
+                any()
+            )
+    }
+
+    @Test
+    fun testUnbindRemovesCachedRemoteView() {
+        // WHEN inflated unbinds content
+        mNotificationInflater.unbindContent(
+            mRow.entry,
+            mRow,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP
+        )
+
+        // THEN inflated informs cache to remove remote view
+        verify(mCache)
+            .removeCachedView(
+                eq(mRow.entry),
+                eq(NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP)
+            )
+    }
+
+    @Test
+    fun testNotificationViewHeightTooSmallFailsValidation() {
+        val validationError =
+            getValidationError(
+                measuredHeightDp = 5f,
+                targetSdk = Build.VERSION_CODES.R,
+                contentView = mock(),
+            )
+        Assert.assertNotNull(validationError)
+    }
+
+    @Test
+    fun testNotificationViewHeightPassesForNewerSDK() {
+        val validationError =
+            getValidationError(
+                measuredHeightDp = 5f,
+                targetSdk = Build.VERSION_CODES.S,
+                contentView = mock(),
+            )
+        Assert.assertNull(validationError)
+    }
+
+    @Test
+    fun testNotificationViewHeightPassesForTemplatedViews() {
+        val validationError =
+            getValidationError(
+                measuredHeightDp = 5f,
+                targetSdk = Build.VERSION_CODES.R,
+                contentView = null,
+            )
+        Assert.assertNull(validationError)
+    }
+
+    @Test
+    fun testNotificationViewPassesValidation() {
+        val validationError =
+            getValidationError(
+                measuredHeightDp = 20f,
+                targetSdk = Build.VERSION_CODES.R,
+                contentView = mock(),
+            )
+        Assert.assertNull(validationError)
+    }
+
+    private fun getValidationError(
+        measuredHeightDp: Float,
+        targetSdk: Int,
+        contentView: RemoteViews?
+    ): String? {
+        val view: View = mock()
+        whenever(view.measuredHeight)
+            .thenReturn(
+                TypedValue.applyDimension(
+                        TypedValue.COMPLEX_UNIT_SP,
+                        measuredHeightDp,
+                        mContext.resources.displayMetrics
+                    )
+                    .toInt()
+            )
+        mRow.entry.targetSdk = targetSdk
+        mRow.entry.sbn.notification.contentView = contentView
+        return NotificationRowContentBinderImpl.isValidView(view, mRow.entry, mContext.resources)
+    }
+
+    @Test
+    fun testInvalidNotificationDoesNotInvokeCallback() {
+        mRow.privateLayout.removeAllViews()
+        mRow.entry.sbn.notification.contentView =
+            RemoteViews(
+                mContext.packageName,
+                com.android.systemui.tests.R.layout.invalid_notification_height
+            )
+        inflateAndWait(
+            true,
+            mNotificationInflater,
+            NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL,
+            mRow
+        )
+        Assert.assertEquals(0, mRow.privateLayout.childCount.toLong())
+        verify(mRow, times(0)).onNotificationUpdated()
+    }
+
+    private class ExceptionHolder {
+        var mException: Exception? = null
+
+        fun setException(exception: Exception?) {
+            mException = exception
+        }
+    }
+
+    private class AsyncFailRemoteView(packageName: String?, layoutId: Int) :
+        RemoteViews(packageName, layoutId) {
+        var mHandler = mockExecutorHandler { p0 -> p0.run() }
+
+        override fun apply(context: Context, parent: ViewGroup): View {
+            return super.apply(context, parent)
+        }
+
+        override fun applyAsync(
+            context: Context,
+            parent: ViewGroup,
+            executor: Executor,
+            listener: OnViewAppliedListener,
+            handler: InteractionHandler?
+        ): CancellationSignal {
+            mHandler.post { listener.onError(RuntimeException("Failed to inflate async")) }
+            return CancellationSignal()
+        }
+
+        override fun applyAsync(
+            context: Context,
+            parent: ViewGroup,
+            executor: Executor,
+            listener: OnViewAppliedListener
+        ): CancellationSignal {
+            return applyAsync(context, parent, executor, listener, null)
+        }
+    }
+
+    companion object {
+        private fun inflateAndWait(
+            inflater: NotificationRowContentBinderImpl,
+            @InflationFlag contentToInflate: Int,
+            row: ExpandableNotificationRow
+        ) {
+            inflateAndWait(false /* expectingException */, inflater, contentToInflate, row)
+        }
+
+        private fun inflateAndWait(
+            expectingException: Boolean,
+            inflater: NotificationRowContentBinderImpl,
+            @InflationFlag contentToInflate: Int,
+            row: ExpandableNotificationRow,
+        ) {
+            val countDownLatch = CountDownLatch(1)
+            val exceptionHolder = ExceptionHolder()
+            inflater.setInflateSynchronously(true)
+            val callback: InflationCallback =
+                object : InflationCallback {
+                    override fun handleInflationException(entry: NotificationEntry, e: Exception) {
+                        if (!expectingException) {
+                            exceptionHolder.setException(e)
+                        }
+                        countDownLatch.countDown()
+                    }
+
+                    override fun onAsyncInflationFinished(entry: NotificationEntry) {
+                        if (expectingException) {
+                            exceptionHolder.setException(
+                                RuntimeException(
+                                    "Inflation finished even though there should be an error"
+                                )
+                            )
+                        }
+                        countDownLatch.countDown()
+                    }
+                }
+            inflater.bindContent(
+                row.entry,
+                row,
+                contentToInflate,
+                BindParams(),
+                false /* forceInflate */,
+                callback /* callback */
+            )
+            Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS))
+            exceptionHolder.mException?.let { throw it }
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 65941ad..21d586b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -84,6 +84,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -192,16 +193,27 @@
                 mBgCoroutineContext,
                 mMainCoroutineContext);
 
-        NotificationRowContentBinder contentBinder = new NotificationContentInflater(
-                mock(NotifRemoteViewCache.class),
-                mock(NotificationRemoteInputManager.class),
-                mock(ConversationNotificationProcessor.class),
-                mock(MediaFeatureFlag.class),
-                mock(Executor.class),
-                new MockSmartReplyInflater(),
-                mock(NotifLayoutInflaterFactory.Provider.class),
-                mock(HeadsUpStyleProvider.class),
-                mock(NotificationRowContentBinderLogger.class));
+        NotificationRowContentBinder contentBinder =
+                NotificationRowContentBinderRefactor.isEnabled()
+                        ? new NotificationRowContentBinderImpl(
+                                mock(NotifRemoteViewCache.class),
+                                mock(NotificationRemoteInputManager.class),
+                                mock(ConversationNotificationProcessor.class),
+                                mock(Executor.class),
+                                new MockSmartReplyInflater(),
+                                mock(NotifLayoutInflaterFactory.Provider.class),
+                                mock(HeadsUpStyleProvider.class),
+                                mock(NotificationRowContentBinderLogger.class))
+                        : new NotificationContentInflater(
+                                mock(NotifRemoteViewCache.class),
+                                mock(NotificationRemoteInputManager.class),
+                                mock(ConversationNotificationProcessor.class),
+                                mock(MediaFeatureFlag.class),
+                                mock(Executor.class),
+                                new MockSmartReplyInflater(),
+                                mock(NotifLayoutInflaterFactory.Provider.class),
+                                mock(HeadsUpStyleProvider.class),
+                                mock(NotificationRowContentBinderLogger.class));
         contentBinder.setInflateSynchronously(true);
         mBindStage = new RowContentBindStage(contentBinder,
                 mock(NotifInflationErrorManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 48e8f88..9b0fd96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -10,6 +10,8 @@
 import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.res.R
@@ -30,8 +32,8 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
 
 /** Tests for {@link NotificationShelf}. */
 @SmallTest
@@ -332,6 +334,144 @@
     }
 
     @Test
+    fun updateState_lastViewAlmostBelowShelf_completelyInShelf() {
+        val viewStart = 0f
+        val shelfClipStart = 0.001f
+
+        val expandableView = mock(ExpandableView::class.java)
+        whenever(expandableView.shelfIcon).thenReturn(mock(StatusBarIconView::class.java))
+        whenever(expandableView.translationY).thenReturn(viewStart)
+        whenever(expandableView.actualHeight).thenReturn(20)
+
+        whenever(expandableView.minHeight).thenReturn(20)
+        whenever(expandableView.shelfTransformationTarget).thenReturn(null) // use translationY
+        whenever(expandableView.isInShelf).thenReturn(true)
+
+        whenever(ambientState.isOnKeyguard).thenReturn(true)
+        whenever(ambientState.isExpansionChanging).thenReturn(false)
+        whenever(ambientState.isShadeExpanded).thenReturn(true)
+
+        val amountInShelf =
+            shelf.getAmountInShelf(
+                /* i= */ 0,
+                /* view= */ expandableView,
+                /* scrollingFast= */ false,
+                /* expandingAnimated= */ false,
+                /* isLastChild= */ true,
+                shelfClipStart
+            )
+        assertEquals(1f, amountInShelf)
+    }
+
+    @Test
+    @EnableSceneContainer
+    fun updateState_withViewInShelf_showShelf() {
+        // GIVEN a view is scrolled into the shelf
+        val stackCutoff = 200f
+        val scrimPadding =
+            context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+        val shelfTop = stackCutoff - scrimPadding - shelf.height
+        val stackScrollAlgorithmState = StackScrollAlgorithmState()
+        val viewInShelf = mock(ExpandableView::class.java)
+
+        whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+        whenever(ambientState.isShadeExpanded).thenReturn(true)
+        whenever(ambientState.lastVisibleBackgroundChild).thenReturn(viewInShelf)
+        whenever(viewInShelf.viewState).thenReturn(ExpandableViewState())
+        whenever(viewInShelf.shelfIcon).thenReturn(mock(StatusBarIconView::class.java))
+        whenever(viewInShelf.translationY).thenReturn(shelfTop)
+        whenever(viewInShelf.actualHeight).thenReturn(10)
+        whenever(viewInShelf.isInShelf).thenReturn(true)
+        whenever(viewInShelf.minHeight).thenReturn(10)
+        whenever(viewInShelf.shelfTransformationTarget).thenReturn(null) // use translationY
+        whenever(viewInShelf.isInShelf).thenReturn(true)
+
+        stackScrollAlgorithmState.visibleChildren.add(viewInShelf)
+        stackScrollAlgorithmState.firstViewInShelf = viewInShelf
+
+        // WHEN Shelf's ViewState is updated
+        shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+        // THEN the shelf is visible, and positioned correctly
+        val shelfState = shelf.viewState as NotificationShelf.ShelfState
+        assertEquals(false, shelfState.hidden)
+        assertEquals(shelf.height, shelfState.height)
+        assertEquals(shelfTop, shelfState.yTranslation)
+    }
+
+    @Test
+    @EnableSceneContainer
+    fun updateState_withViewInShelfDuringExpansion_showShelf() {
+        // GIVEN a view is scrolled into the shelf
+        val stackCutoff = 200f
+        val scrimPadding =
+            context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+        val stackBottom = stackCutoff - scrimPadding
+        val shelfTop = stackBottom - shelf.height
+        val stackScrollAlgorithmState = StackScrollAlgorithmState()
+        val viewInShelf = mock(ExpandableView::class.java)
+
+        // AND a shade expansion is in progress
+        val shadeExpansionFraction = 0.5f
+
+        whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+        whenever(ambientState.isShadeExpanded).thenReturn(true)
+        whenever(ambientState.lastVisibleBackgroundChild).thenReturn(viewInShelf)
+        whenever(ambientState.isExpansionChanging).thenReturn(true)
+        whenever(ambientState.expansionFraction).thenReturn(shadeExpansionFraction)
+        whenever(viewInShelf.viewState).thenReturn(ExpandableViewState())
+        whenever(viewInShelf.shelfIcon).thenReturn(mock(StatusBarIconView::class.java))
+        whenever(viewInShelf.translationY).thenReturn(shelfTop)
+        whenever(viewInShelf.actualHeight).thenReturn(10)
+        whenever(viewInShelf.isInShelf).thenReturn(true)
+        whenever(viewInShelf.minHeight).thenReturn(10)
+        whenever(viewInShelf.shelfTransformationTarget).thenReturn(null) // use translationY
+        whenever(viewInShelf.isInShelf).thenReturn(true)
+
+        stackScrollAlgorithmState.visibleChildren.add(viewInShelf)
+        stackScrollAlgorithmState.firstViewInShelf = viewInShelf
+
+        // WHEN Shelf's ViewState is updated
+        shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+        // THEN the shelf is visible
+        val shelfState = shelf.viewState as NotificationShelf.ShelfState
+        assertEquals(false, shelfState.hidden)
+        assertEquals(shelf.height, shelfState.height)
+        // AND its translation is scaled by the shade expansion
+        assertEquals((stackBottom * 0.75f) - shelf.height, shelfState.yTranslation)
+    }
+
+    @Test
+    @EnableSceneContainer
+    fun updateState_withNullLastVisibleBackgroundChild_hideShelf_withSceneContainer() {
+        // GIVEN
+        val stackCutoff = 200f
+        val scrimPadding =
+            context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+        val paddingBetweenElements =
+            context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+        whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+        whenever(ambientState.isShadeExpanded).thenReturn(true)
+        val lastVisibleBackgroundChild = mock<ExpandableView>()
+        val expandableViewState = ExpandableViewState()
+        whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+        val stackScrollAlgorithmState = StackScrollAlgorithmState()
+        stackScrollAlgorithmState.firstViewInShelf = mock()
+
+        whenever(ambientState.lastVisibleBackgroundChild).thenReturn(null)
+
+        // WHEN
+        shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+        // THEN
+        val shelfState = shelf.viewState as NotificationShelf.ShelfState
+        assertEquals(true, shelfState.hidden)
+        assertEquals(stackCutoff - scrimPadding + paddingBetweenElements, shelfState.yTranslation)
+    }
+
+    @Test
+    @DisableSceneContainer
     fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
         // GIVEN
         whenever(ambientState.stackY).thenReturn(100f)
@@ -358,6 +498,35 @@
     }
 
     @Test
+    @EnableSceneContainer
+    fun updateState_withNullFirstViewInShelf_hideShelf_withSceneContainer() {
+        // GIVEN
+        val stackCutoff = 200f
+        val scrimPadding =
+            context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+        val paddingBetweenElements =
+            context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+        whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+        whenever(ambientState.isShadeExpanded).thenReturn(true)
+        val lastVisibleBackgroundChild = mock<ExpandableView>()
+        val expandableViewState = ExpandableViewState()
+        whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+        whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+        val stackScrollAlgorithmState = StackScrollAlgorithmState()
+
+        stackScrollAlgorithmState.firstViewInShelf = null
+
+        // WHEN
+        shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+        // THEN
+        val shelfState = shelf.viewState as NotificationShelf.ShelfState
+        assertEquals(true, shelfState.hidden)
+        assertEquals(stackCutoff - scrimPadding + paddingBetweenElements, shelfState.yTranslation)
+    }
+
+    @Test
+    @DisableSceneContainer
     fun updateState_withNullFirstViewInShelf_hideShelf() {
         // GIVEN
         whenever(ambientState.stackY).thenReturn(100f)
@@ -384,6 +553,35 @@
     }
 
     @Test
+    @EnableSceneContainer
+    fun updateState_withCollapsedShade_hideShelf_withSceneContainer() {
+        // GIVEN
+        val stackCutoff = 200f
+        val scrimPadding =
+            context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+        val paddingBetweenElements =
+            context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+        whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+        val lastVisibleBackgroundChild = mock<ExpandableView>()
+        val expandableViewState = ExpandableViewState()
+        whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+        whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+        val stackScrollAlgorithmState = StackScrollAlgorithmState()
+        stackScrollAlgorithmState.firstViewInShelf = mock()
+
+        whenever(ambientState.isShadeExpanded).thenReturn(false)
+
+        // WHEN
+        shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+        // THEN
+        val shelfState = shelf.viewState as NotificationShelf.ShelfState
+        assertEquals(true, shelfState.hidden)
+        assertEquals(stackCutoff - scrimPadding + paddingBetweenElements, shelfState.yTranslation)
+    }
+
+    @Test
+    @DisableSceneContainer
     fun updateState_withCollapsedShade_hideShelf() {
         // GIVEN
         whenever(ambientState.stackY).thenReturn(100f)
@@ -410,6 +608,49 @@
     }
 
     @Test
+    @EnableSceneContainer
+    fun updateState_withHiddenSectionBeforeShelf_hideShelf_withSceneContianer() {
+        // GIVEN
+        val stackCutoff = 200f
+        whenever(ambientState.stackCutoff).thenReturn(stackCutoff)
+        val scrimPadding =
+            context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings)
+        val paddingBetweenElements =
+            context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
+        whenever(ambientState.isShadeExpanded).thenReturn(true)
+        val lastVisibleBackgroundChild = mock<ExpandableView>()
+        val expandableViewState = ExpandableViewState()
+        whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState)
+        val stackScrollAlgorithmState = StackScrollAlgorithmState()
+        whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild)
+
+        val ssaVisibleChild = mock<ExpandableView>()
+        val ssaVisibleChildState = ExpandableViewState()
+        ssaVisibleChildState.hidden = true
+        whenever(ssaVisibleChild.viewState).thenReturn(ssaVisibleChildState)
+
+        val ssaVisibleChild1 = mock<ExpandableView>()
+        val ssaVisibleChildState1 = ExpandableViewState()
+        ssaVisibleChildState1.hidden = true
+        whenever(ssaVisibleChild1.viewState).thenReturn(ssaVisibleChildState1)
+
+        stackScrollAlgorithmState.visibleChildren.add(ssaVisibleChild)
+        stackScrollAlgorithmState.visibleChildren.add(ssaVisibleChild1)
+        whenever(ambientState.isExpansionChanging).thenReturn(true)
+        whenever(ambientState.expansionFraction).thenReturn(1f)
+        stackScrollAlgorithmState.firstViewInShelf = ssaVisibleChild1
+
+        // WHEN
+        shelf.updateState(stackScrollAlgorithmState, ambientState)
+
+        // THEN
+        val shelfState = shelf.viewState as NotificationShelf.ShelfState
+        assertEquals(true, shelfState.hidden)
+        assertEquals(stackCutoff - scrimPadding + paddingBetweenElements, shelfState.yTranslation)
+    }
+
+    @Test
+    @DisableSceneContainer
     fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
         // GIVEN
         whenever(ambientState.stackY).thenReturn(100f)
@@ -461,12 +702,9 @@
         expectedAlpha: Float
     ) {
         val sbnMock: StatusBarNotification = mock()
-        val mockEntry = mock<NotificationEntry>().apply {
-            whenever(this.sbn).thenReturn(sbnMock)
-        }
+        val mockEntry = mock<NotificationEntry>().apply { whenever(this.sbn).thenReturn(sbnMock) }
         val row = ExpandableNotificationRow(mContext, null, mockEntry)
-        whenever(ambientState.lastVisibleBackgroundChild)
-            .thenReturn(row)
+        whenever(ambientState.lastVisibleBackgroundChild).thenReturn(row)
         whenever(ambientState.isExpansionChanging).thenReturn(true)
         whenever(ambientState.expansionFraction).thenReturn(expansionFraction)
         whenever(hostLayoutController.speedBumpIndex).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index f0bc655..665544d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -61,7 +61,7 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.CastDevice;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DeviceControlsController;
 import com.android.systemui.statusbar.policy.HotspotController;
@@ -422,9 +422,17 @@
     }
 
     private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
-        CastDevice cd = new CastDevice();
-        cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED;
-        return Collections.singletonList(cd);
+        CastDevice.CastState state = isCasting
+                ? CastDevice.CastState.Connected
+                : CastDevice.CastState.Disconnected;
+        return Collections.singletonList(
+                new CastDevice(
+                        "id",
+                        /* name= */ null,
+                        /* description= */ null,
+                        /* state= */ state,
+                        /* origin= */ CastDevice.CastOrigin.MediaProjection,
+                        /* tag= */ null));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 5675915..5e5586d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -40,6 +40,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSHost;
@@ -101,6 +102,7 @@
     @Mock private UserTracker mUserTracker;
     @Mock private QSHost mQSHost;
     @Mock private ActivityStarter mActivityStarter;
+    @Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
 
     CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
 
@@ -137,7 +139,8 @@
                 mCameraLauncherLazy,
                 mUserTracker,
                 mQSHost,
-                mActivityStarter);
+                mActivityStarter,
+                mEmergencyGestureIntentFactory);
 
         when(mUserTracker.getUserHandle()).thenReturn(
                 UserHandle.of(ActivityManager.getCurrentUser()));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 7ea85a1..1eb33ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -112,6 +112,7 @@
 import com.android.systemui.communal.shared.model.CommunalScenes;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory;
 import com.android.systemui.flags.DisableSceneContainer;
 import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.flags.FakeFeatureFlags;
@@ -339,6 +340,7 @@
     @Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
     @Mock private PackageManager mPackageManager;
     @Mock private GlanceableHubContainerController mGlanceableHubContainerController;
+    @Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
 
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -596,7 +598,8 @@
                 () -> mFingerprintManager,
                 mActivityStarter,
                 mBrightnessMirrorShowingInteractor,
-                mGlanceableHubContainerController
+                mGlanceableHubContainerController,
+                mEmergencyGestureIntentFactory
         );
         mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
         mCentralSurfaces.initShadeVisibilityListener();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index f3d6407..53e643e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -223,7 +223,8 @@
                 mFakeExecutor,
                 mBackgroundExecutor,
                 mLogger,
-                mStatusOverlayHoverListenerFactory
+                mStatusOverlayHoverListenerFactory,
+                mKosmos.getCommunalSceneInteractor()
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
index 43c19b8..7dfdb92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.os.Handler;
 import android.testing.TestableLooper;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -58,14 +59,16 @@
     private KeyguardStateController mKeyguardStateController;
     @Mock
     private StatusBarStateController mStatusBarStateController;
+    @Mock
+    private Handler mBgHandler;
 
     private LightBarTransitionsController mLightBarTransitionsController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mLightBarTransitionsController = new LightBarTransitionsController(mContext, mApplier,
-                new CommandQueue(mContext, new FakeDisplayTracker(mContext)),
+        mLightBarTransitionsController = new LightBarTransitionsController(mContext,
+                mBgHandler, mApplier, new CommandQueue(mContext, new FakeDisplayTracker(mContext)),
                 mKeyguardStateController, mStatusBarStateController);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 4d6798b..7273d9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -32,17 +32,19 @@
 import android.widget.LinearLayout
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
 import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
@@ -60,13 +62,13 @@
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.ArgumentMatchers.nullable
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 private const val CALL_UID = 900
@@ -93,8 +95,8 @@
     private lateinit var controller: OngoingCallController
     private lateinit var notifCollectionListener: NotifCollectionListener
 
-    @Mock private lateinit var mockSwipeStatusBarAwayGestureHandler:
-        SwipeStatusBarAwayGestureHandler
+    @Mock
+    private lateinit var mockSwipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler
     @Mock private lateinit var mockOngoingCallListener: OngoingCallListener
     @Mock private lateinit var mockActivityStarter: ActivityStarter
     @Mock private lateinit var mockIActivityManager: IActivityManager
@@ -112,21 +114,22 @@
         MockitoAnnotations.initMocks(this)
         val notificationCollection = mock(CommonNotifCollection::class.java)
 
-        controller = OngoingCallController(
-            testScope.backgroundScope,
-            context,
-            ongoingCallRepository,
-            notificationCollection,
-            clock,
-            mockActivityStarter,
-            mainExecutor,
-            mockIActivityManager,
-            OngoingCallLogger(uiEventLoggerFake),
-            DumpManager(),
-            mockStatusBarWindowController,
-            mockSwipeStatusBarAwayGestureHandler,
-            statusBarModeRepository,
-        )
+        controller =
+            OngoingCallController(
+                testScope.backgroundScope,
+                context,
+                ongoingCallRepository,
+                notificationCollection,
+                clock,
+                mockActivityStarter,
+                mainExecutor,
+                mockIActivityManager,
+                OngoingCallLogger(uiEventLoggerFake),
+                DumpManager(),
+                mockStatusBarWindowController,
+                mockSwipeStatusBarAwayGestureHandler,
+                statusBarModeRepository,
+            )
         controller.start()
         controller.addCallback(mockOngoingCallListener)
         controller.setChipView(chipView)
@@ -136,7 +139,7 @@
         notifCollectionListener = collectionListenerCaptor.value!!
 
         `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
-                .thenReturn(PROC_STATE_INVISIBLE)
+            .thenReturn(PROC_STATE_INVISIBLE)
     }
 
     @After
@@ -146,10 +149,14 @@
 
     @Test
     fun onEntryUpdated_isOngoingCallNotif_listenerAndRepoNotified() {
-        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+        val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
+        notification.modifyNotification(context).setWhen(567)
+        notifCollectionListener.onEntryUpdated(notification.build())
 
         verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
-        assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+        val repoState = ongoingCallRepository.ongoingCallState.value
+        assertThat(repoState).isInstanceOf(OngoingCallModel.InCall::class.java)
+        assertThat((repoState as OngoingCallModel.InCall).startTimeMs).isEqualTo(567)
     }
 
     @Test
@@ -164,7 +171,8 @@
         notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
 
         verify(mockOngoingCallListener, never()).onOngoingCallStateChanged(anyBoolean())
-        assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.NoCall::class.java)
     }
 
     @Test
@@ -172,73 +180,77 @@
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
         notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
 
-        verify(mockOngoingCallListener, times(2))
-                .onOngoingCallStateChanged(anyBoolean())
+        verify(mockOngoingCallListener, times(2)).onOngoingCallStateChanged(anyBoolean())
     }
 
     @Test
     fun onEntryUpdated_ongoingCallNotifThenScreeningCallNotif_repoUpdated() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
-        assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.InCall::class.java)
 
         notifCollectionListener.onEntryUpdated(createScreeningCallNotifEntry())
 
-        assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.NoCall::class.java)
     }
 
     /** Regression test for b/191472854. */
     @Test
     fun onEntryUpdated_notifHasNullContentIntent_noCrash() {
         notifCollectionListener.onEntryUpdated(
-                createCallNotifEntry(ongoingCallStyle, nullContentIntent = true))
+            createCallNotifEntry(ongoingCallStyle, nullContentIntent = true)
+        )
     }
 
     /** Regression test for b/192379214. */
     @Test
-    @DisableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME)
+    @DisableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME, FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     fun onEntryUpdated_notificationWhenIsZero_timeHidden() {
         val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
         notification.modifyNotification(context).setWhen(0)
 
         notifCollectionListener.onEntryUpdated(notification.build())
         chipView.measure(
-                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
-                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
         )
 
         assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
-                .isEqualTo(0)
+            .isEqualTo(0)
     }
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_SORT_SECTION_BY_TIME)
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     fun onEntryUpdated_notificationWhenIsZero_timeShown() {
         val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
         notification.modifyNotification(context).setWhen(0)
 
         notifCollectionListener.onEntryUpdated(notification.build())
         chipView.measure(
-                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
-                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
         )
 
         assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
-                .isGreaterThan(0)
+            .isGreaterThan(0)
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     fun onEntryUpdated_notificationWhenIsValid_timeShown() {
         val notification = NotificationEntryBuilder(createOngoingCallNotifEntry())
         notification.modifyNotification(context).setWhen(clock.currentTimeMillis())
 
         notifCollectionListener.onEntryUpdated(notification.build())
         chipView.measure(
-                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
-                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
         )
 
         assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
-                .isGreaterThan(0)
+            .isGreaterThan(0)
     }
 
     /** Regression test for b/194731244. */
@@ -250,15 +262,14 @@
             notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
         }
 
-        verify(mockIActivityManager, times(1))
-            .registerUidObserver(any(), any(), any(), any())
+        verify(mockIActivityManager, times(1)).registerUidObserver(any(), any(), any(), any())
     }
 
     /** Regression test for b/216248574. */
     @Test
     fun entryUpdated_getUidProcessStateThrowsException_noCrash() {
         `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
-                .thenThrow(SecurityException())
+            .thenThrow(SecurityException())
 
         // No assert required, just check no crash
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -267,9 +278,15 @@
     /** Regression test for b/216248574. */
     @Test
     fun entryUpdated_registerUidObserverThrowsException_noCrash() {
-        `when`(mockIActivityManager.registerUidObserver(
-            any(), any(), any(), nullable(String::class.java)
-        )).thenThrow(SecurityException())
+        `when`(
+                mockIActivityManager.registerUidObserver(
+                    any(),
+                    any(),
+                    any(),
+                    nullable(String::class.java),
+                )
+            )
+            .thenThrow(SecurityException())
 
         // No assert required, just check no crash
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
@@ -281,9 +298,8 @@
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
         val packageNameCaptor = ArgumentCaptor.forClass(String::class.java)
-        verify(mockIActivityManager).registerUidObserver(
-            any(), any(), any(), packageNameCaptor.capture()
-        )
+        verify(mockIActivityManager)
+            .registerUidObserver(any(), any(), any(), packageNameCaptor.capture())
         assertThat(packageNameCaptor.value).isNotNull()
     }
 
@@ -313,11 +329,13 @@
     fun onEntryRemoved_callNotifAddedThenRemoved_repoUpdated() {
         val ongoingCallNotifEntry = createOngoingCallNotifEntry()
         notifCollectionListener.onEntryAdded(ongoingCallNotifEntry)
-        assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.InCall::class.java)
 
         notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
 
-        assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.NoCall::class.java)
     }
 
     @Test
@@ -360,7 +378,8 @@
 
         notifCollectionListener.onEntryRemoved(removedEntryBuilder.build(), REASON_USER_STOPPED)
 
-        assertThat(ongoingCallRepository.hasOngoingCall.value).isFalse()
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.NoCall::class.java)
     }
 
     @Test
@@ -379,7 +398,8 @@
 
         notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
 
-        assertThat(ongoingCallRepository.hasOngoingCall.value).isTrue()
+        assertThat(ongoingCallRepository.ongoingCallState.value)
+            .isInstanceOf(OngoingCallModel.InCall::class.java)
     }
 
     @Test
@@ -404,7 +424,7 @@
     @Test
     fun hasOngoingCall_ongoingCallNotifSentAndCallAppNotVisible_returnsTrue() {
         `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
-                .thenReturn(PROC_STATE_INVISIBLE)
+            .thenReturn(PROC_STATE_INVISIBLE)
 
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
@@ -414,7 +434,7 @@
     @Test
     fun hasOngoingCall_ongoingCallNotifSentButCallAppVisible_returnsFalse() {
         `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
-                .thenReturn(PROC_STATE_VISIBLE)
+            .thenReturn(PROC_STATE_VISIBLE)
 
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
@@ -472,10 +492,8 @@
 
         lateinit var newChipView: View
         TestableLooper.get(this).runWithLooper {
-            newChipView = LayoutInflater.from(mContext).inflate(
-                    R.layout.ongoing_activity_chip,
-                    null
-            )
+            newChipView =
+                LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
         }
 
         // Change the chip view associated with the controller.
@@ -488,13 +506,13 @@
     fun callProcessChangesToVisible_listenerNotified() {
         // Start the call while the process is invisible.
         `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
-                .thenReturn(PROC_STATE_INVISIBLE)
+            .thenReturn(PROC_STATE_INVISIBLE)
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
         reset(mockOngoingCallListener)
 
         val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
-        verify(mockIActivityManager).registerUidObserver(
-                captor.capture(), any(), any(), nullable(String::class.java))
+        verify(mockIActivityManager)
+            .registerUidObserver(captor.capture(), any(), any(), nullable(String::class.java))
         val uidObserver = captor.value
 
         // Update the process to visible.
@@ -509,13 +527,13 @@
     fun callProcessChangesToInvisible_listenerNotified() {
         // Start the call while the process is visible.
         `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java)))
-                .thenReturn(PROC_STATE_VISIBLE)
+            .thenReturn(PROC_STATE_VISIBLE)
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
         reset(mockOngoingCallListener)
 
         val captor = ArgumentCaptor.forClass(IUidObserver.Stub::class.java)
-        verify(mockIActivityManager).registerUidObserver(
-                captor.capture(), any(), any(), nullable(String::class.java))
+        verify(mockIActivityManager)
+            .registerUidObserver(captor.capture(), any(), any(), nullable(String::class.java))
         val uidObserver = captor.value
 
         // Update the process to invisible.
@@ -527,6 +545,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     fun chipClicked_clickEventLogged() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
@@ -534,11 +553,12 @@
 
         assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
         assertThat(uiEventLoggerFake.eventId(0))
-                .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
+            .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_CLICKED.id)
     }
 
     /** Regression test for b/212467440. */
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     fun chipClicked_activityStarterTriggeredWithUnmodifiedIntent() {
         val notifEntry = createOngoingCallNotifEntry()
         val pendingIntent = notifEntry.sbn.notification.contentIntent
@@ -556,12 +576,14 @@
 
         assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
         assertThat(uiEventLoggerFake.eventId(0))
-                .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
+            .isEqualTo(OngoingCallLogger.OngoingCallEvents.ONGOING_CALL_VISIBLE.id)
     }
+
     // Other tests for notifyChipVisibilityChanged are in [OngoingCallLogger], since
     // [OngoingCallController.notifyChipVisibilityChanged] just delegates to that class.
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     fun callNotificationAdded_chipIsClickable() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
 
@@ -569,6 +591,15 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    fun callNotificationAdded_newChipsEnabled_chipNotClickable() {
+        notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+        assertThat(chipView.hasOnClickListeners()).isFalse()
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     fun fullscreenIsTrue_chipStillClickable() {
         notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
         statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = true
@@ -621,8 +652,7 @@
         statusBarModeRepository.defaultDisplay.isInFullscreenMode.value = false
         testScope.runCurrent()
 
-        verify(mockSwipeStatusBarAwayGestureHandler)
-            .removeOnGestureDetectedCallback(anyString())
+        verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(anyString())
     }
 
     @Test
@@ -635,8 +665,7 @@
 
         notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
 
-        verify(mockSwipeStatusBarAwayGestureHandler)
-            .removeOnGestureDetectedCallback(anyString())
+        verify(mockSwipeStatusBarAwayGestureHandler).removeOnGestureDetectedCallback(anyString())
     }
 
     // TODO(b/195839150): Add test
@@ -675,5 +704,9 @@
 private val hangUpIntent = mock(PendingIntent::class.java)
 
 private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(person, hangUpIntent)
-private val screeningCallStyle = Notification.CallStyle.forScreeningCall(
-        person, hangUpIntent, /* answerIntent= */ mock(PendingIntent::class.java))
\ No newline at end of file
+private val screeningCallStyle =
+    Notification.CallStyle.forScreeningCall(
+        person,
+        hangUpIntent,
+        /* answerIntent= */ mock(PendingIntent::class.java),
+    )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
index 56aa7d6..d6624ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
@@ -27,12 +28,13 @@
 
     @Test
     fun hasOngoingCall_matchesSet() {
-        underTest.setHasOngoingCall(true)
+        val inCallModel = OngoingCallModel.InCall(startTimeMs = 654, intent = null)
+        underTest.setOngoingCallState(inCallModel)
 
-        assertThat(underTest.hasOngoingCall.value).isTrue()
+        assertThat(underTest.ongoingCallState.value).isEqualTo(inCallModel)
 
-        underTest.setHasOngoingCall(false)
+        underTest.setOngoingCallState(OngoingCallModel.NoCall)
 
-        assertThat(underTest.hasOngoingCall.value).isFalse()
+        assertThat(underTest.ongoingCallState.value).isEqualTo(OngoingCallModel.NoCall)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
index 617c553..7e523fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
@@ -86,6 +86,7 @@
                 /* iconLevel= */ 0,
                 /* number= */ 0,
                 "contentDescription",
+                StatusBarIcon.Type.SystemIcon,
             )
         underTest.setIconFromTile(slotName, externalIcon)
 
@@ -114,6 +115,7 @@
                 /* iconLevel= */ 0,
                 /* number= */ 0,
                 "contentDescription",
+                StatusBarIcon.Type.SystemIcon,
             )
         commandQueueCallbacks.setIcon(slotName, externalIcon)
 
@@ -240,6 +242,7 @@
                 /* iconLevel= */ 0,
                 /* number= */ 0,
                 "externalDescription",
+                StatusBarIcon.Type.SystemIcon,
             )
         underTest.setIconFromTile(slotName, startingExternalIcon)
 
@@ -278,6 +281,7 @@
                 /* iconLevel= */ 0,
                 /* number= */ 0,
                 "externalDescription",
+                StatusBarIcon.Type.SystemIcon,
             )
         underTest.setIconFromTile(slotName, startingExternalIcon)
 
@@ -290,6 +294,7 @@
                 /* iconLevel= */ 0,
                 /* number= */ 0,
                 "newExternalDescription",
+                StatusBarIcon.Type.SystemIcon,
             )
         underTest.setIconFromTile(slotName, newExternalIcon)
 
@@ -325,6 +330,7 @@
                 /* iconLevel= */ 0,
                 /* number= */ 0,
                 "externalDescription",
+                StatusBarIcon.Type.SystemIcon,
             )
         commandQueueCallbacks.setIcon(slotName, startingExternalIcon)
 
@@ -337,6 +343,7 @@
                 /* iconLevel= */ 0,
                 /* number= */ 0,
                 "newExternalDescription",
+                StatusBarIcon.Type.SystemIcon,
             )
         commandQueueCallbacks.setIcon(slotName, newExternalIcon)
 
@@ -404,6 +411,7 @@
             /* iconLevel= */ 0,
             /* number= */ 0,
             "contentDescription",
+            StatusBarIcon.Type.SystemIcon,
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
index 95b132d..3de50c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt
@@ -19,6 +19,7 @@
 import android.os.PersistableBundle
 import android.telephony.CarrierConfigManager
 import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL
 import android.telephony.CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -53,16 +54,19 @@
     fun processNewConfig_updatesAllFlows() {
         assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
         assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
+        assertThat(underTest.allowNetworkSliceIndicator.value).isTrue()
 
         underTest.processNewCarrierConfig(
             configWithOverrides(
                 KEY_INFLATE_SIGNAL_STRENGTH_BOOL to true,
                 KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL to true,
+                KEY_SHOW_5G_SLICE_ICON_BOOL to false,
             )
         )
 
         assertThat(underTest.shouldInflateSignalStrength.value).isTrue()
         assertThat(underTest.showOperatorNameInStatusBar.value).isTrue()
+        assertThat(underTest.allowNetworkSliceIndicator.value).isFalse()
     }
 
     @Test
@@ -79,12 +83,14 @@
                 configWithOverrides(
                     KEY_INFLATE_SIGNAL_STRENGTH_BOOL to true,
                     KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL to true,
+                    KEY_SHOW_5G_SLICE_ICON_BOOL to true,
                 )
             )
 
         assertThat(underTest.isUsingDefault).isTrue()
         assertThat(underTest.shouldInflateSignalStrength.value).isTrue()
         assertThat(underTest.showOperatorNameInStatusBar.value).isTrue()
+        assertThat(underTest.allowNetworkSliceIndicator.value).isTrue()
 
         // Process a new config with no keys
         underTest.processNewCarrierConfig(PersistableBundle())
@@ -92,6 +98,7 @@
         assertThat(underTest.isUsingDefault).isFalse()
         assertThat(underTest.shouldInflateSignalStrength.value).isFalse()
         assertThat(underTest.showOperatorNameInStatusBar.value).isFalse()
+        assertThat(underTest.allowNetworkSliceIndicator.value).isFalse()
     }
 
     companion object {
@@ -105,6 +112,7 @@
             PersistableBundle().also {
                 it.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false)
                 it.putBoolean(CarrierConfigManager.KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR_BOOL, false)
+                it.putBoolean(CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL, true)
             }
 
         /** Override the default config with the given (key, value) pair */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 3695d8c..6d8bf55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -25,6 +25,7 @@
 import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN
 import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN
 import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
+import android.telephony.CarrierConfigManager.KEY_SHOW_5G_SLICE_ICON_BOOL
 import android.telephony.NetworkRegistrationInfo
 import android.telephony.NetworkRegistrationInfo.DOMAIN_PS
 import android.telephony.NetworkRegistrationInfo.REGISTRATION_STATE_DENIED
@@ -1044,6 +1045,24 @@
         }
 
     @Test
+    fun allowNetworkSliceIndicator_exposesCarrierConfigValue() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.allowNetworkSliceIndicator)
+
+            systemUiCarrierConfig.processNewCarrierConfig(
+                configWithOverride(KEY_SHOW_5G_SLICE_ICON_BOOL, true)
+            )
+
+            assertThat(latest).isTrue()
+
+            systemUiCarrierConfig.processNewCarrierConfig(
+                configWithOverride(KEY_SHOW_5G_SLICE_ICON_BOOL, false)
+            )
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
     fun isAllowedDuringAirplaneMode_alwaysFalse() =
         testScope.runTest {
             val latest by collectLastValue(underTest.isAllowedDuringAirplaneMode)
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 dfe8023..1488418 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
@@ -194,6 +194,50 @@
         }
 
     @Test
+    fun networkSlice_configOn_hasPrioritizedCaps_showsSlice() =
+        testScope.runTest {
+            connectionRepository.allowNetworkSliceIndicator.value = true
+            val latest by collectLastValue(underTest.showSliceAttribution)
+
+            connectionRepository.hasPrioritizedNetworkCapabilities.value = true
+
+            assertThat(latest).isTrue()
+        }
+
+    @Test
+    fun networkSlice_configOn_noPrioritizedCaps_noSlice() =
+        testScope.runTest {
+            connectionRepository.allowNetworkSliceIndicator.value = true
+            val latest by collectLastValue(underTest.showSliceAttribution)
+
+            connectionRepository.hasPrioritizedNetworkCapabilities.value = false
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun networkSlice_configOff_hasPrioritizedCaps_noSlice() =
+        testScope.runTest {
+            connectionRepository.allowNetworkSliceIndicator.value = false
+            val latest by collectLastValue(underTest.showSliceAttribution)
+
+            connectionRepository.hasPrioritizedNetworkCapabilities.value = true
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
+    fun networkSlice_configOff_noPrioritizedCaps_noSlice() =
+        testScope.runTest {
+            connectionRepository.allowNetworkSliceIndicator.value = false
+            val latest by collectLastValue(underTest.showSliceAttribution)
+
+            connectionRepository.hasPrioritizedNetworkCapabilities.value = false
+
+            assertThat(latest).isFalse()
+        }
+
+    @Test
     fun iconGroup_three_g() =
         testScope.runTest {
             connectionRepository.resolvedNetworkType.value =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index d24d87c6..890a2e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -34,6 +34,7 @@
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
 import android.telephony.satellite.SatelliteManager.SatelliteException
 import android.telephony.satellite.SatelliteModemStateCallback
+import android.telephony.satellite.SatelliteProvisionStateCallback
 import android.telephony.satellite.SatelliteSupportedStateCallback
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -326,6 +327,98 @@
         }
 
     @Test
+    fun satelliteProvisioned_notSupported_defaultFalse() =
+        testScope.runTest {
+            // GIVEN satellite is not supported
+            setUpRepo(
+                uptime = MIN_UPTIME,
+                satMan = satelliteManager,
+                satelliteSupported = false,
+            )
+
+            assertThat(underTest.isSatelliteProvisioned.value).isFalse()
+        }
+
+    @Test
+    fun satelliteProvisioned_supported_defaultFalse() =
+        testScope.runTest {
+            // GIVEN satellite is supported
+            setUpRepo(
+                uptime = MIN_UPTIME,
+                satMan = satelliteManager,
+                satelliteSupported = true,
+            )
+
+            // THEN default provisioned state is false
+            assertThat(underTest.isSatelliteProvisioned.value).isFalse()
+        }
+
+    @Test
+    fun satelliteProvisioned_supported_tracksCallback() =
+        testScope.runTest {
+            // GIVEN satellite is not supported
+            setUpRepo(
+                uptime = MIN_UPTIME,
+                satMan = satelliteManager,
+                satelliteSupported = true,
+            )
+
+            val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+            runCurrent()
+
+            val callback =
+                withArgCaptor<SatelliteProvisionStateCallback> {
+                    verify(satelliteManager).registerForProvisionStateChanged(any(), capture())
+                }
+
+            // WHEN provisioning state changes
+            callback.onSatelliteProvisionStateChanged(true)
+
+            // THEN the value is reflected in the repo
+            assertThat(provisioned).isTrue()
+        }
+
+    @Test
+    fun satelliteProvisioned_supported_tracksCallback_reRegistersOnCrash() =
+        testScope.runTest {
+            // GIVEN satellite is supported
+            setUpRepo(
+                uptime = MIN_UPTIME,
+                satMan = satelliteManager,
+                satelliteSupported = true,
+            )
+
+            val provisioned by collectLastValue(underTest.isSatelliteProvisioned)
+
+            runCurrent()
+
+            val callback =
+                withArgCaptor<SatelliteProvisionStateCallback> {
+                    verify(satelliteManager).registerForProvisionStateChanged(any(), capture())
+                }
+            val telephonyCallback =
+                MobileTelephonyHelpers.getTelephonyCallbackForType<
+                    TelephonyCallback.RadioPowerStateListener
+                >(
+                    telephonyManager
+                )
+
+            // GIVEN satellite is currently provisioned
+            callback.onSatelliteProvisionStateChanged(true)
+
+            assertThat(provisioned).isTrue()
+
+            // WHEN a crash event happens (detected by radio state change)
+            telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
+            runCurrent()
+            telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
+            runCurrent()
+
+            // THEN listeners are re-registered
+            verify(satelliteManager, times(2)).registerForProvisionStateChanged(any(), any())
+        }
+
+    @Test
     fun satelliteNotSupported_listenersAreNotRegistered() =
         testScope.runTest {
             // GIVEN satellite is not supported
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
index 5fa2d33..55460bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/FakeDeviceBasedSatelliteRepository.kt
@@ -21,6 +21,8 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 
 class FakeDeviceBasedSatelliteRepository() : DeviceBasedSatelliteRepository {
+    override val isSatelliteProvisioned = MutableStateFlow(true)
+
     override val connectionState = MutableStateFlow(Off)
 
     override val signalStrength = MutableStateFlow(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index d303976..2e5ebb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -31,8 +31,6 @@
 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
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
-import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -55,9 +53,6 @@
         )
 
     private val repo = FakeDeviceBasedSatelliteRepository()
-    private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
-    private val deviceProvisioningInteractor =
-        DeviceProvisioningInteractor(deviceProvisionedRepository)
     private val connectivityRepository = FakeConnectivityRepository()
     private val wifiRepository = FakeWifiRepository()
     private val wifiInteractor =
@@ -69,7 +64,6 @@
             DeviceBasedSatelliteInteractor(
                 repo,
                 iconsInteractor,
-                deviceProvisioningInteractor,
                 wifiInteractor,
                 testScope.backgroundScope,
                 FakeLogBuffer.Factory.create(),
@@ -113,7 +107,6 @@
                 DeviceBasedSatelliteInteractor(
                     repo,
                     iconsInteractor,
-                    deviceProvisioningInteractor,
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
@@ -162,7 +155,6 @@
                 DeviceBasedSatelliteInteractor(
                     repo,
                     iconsInteractor,
-                    deviceProvisioningInteractor,
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
@@ -219,7 +211,6 @@
                 DeviceBasedSatelliteInteractor(
                     repo,
                     iconsInteractor,
-                    deviceProvisioningInteractor,
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
@@ -538,7 +529,6 @@
                 DeviceBasedSatelliteInteractor(
                     repo,
                     iconsInteractor,
-                    deviceProvisioningInteractor,
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 43b9568..c39e301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -32,8 +32,6 @@
 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
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
-import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
@@ -55,9 +53,6 @@
 
     private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
 
-    private val deviceProvisionedRepository = FakeDeviceProvisioningRepository()
-    private val deviceProvisioningInteractor =
-        DeviceProvisioningInteractor(deviceProvisionedRepository)
     private val connectivityRepository = FakeConnectivityRepository()
     private val wifiRepository = FakeWifiRepository()
     private val wifiInteractor =
@@ -72,7 +67,6 @@
             DeviceBasedSatelliteInteractor(
                 repo,
                 mobileIconsInteractor,
-                deviceProvisioningInteractor,
                 wifiInteractor,
                 testScope.backgroundScope,
                 FakeLogBuffer.Factory.create(),
@@ -252,14 +246,14 @@
             // GIVEN apm is disabled
             airplaneModeRepository.setIsAirplaneMode(false)
 
-            // GIVEN device is not provisioned
-            deviceProvisionedRepository.setDeviceProvisioned(false)
+            // GIVEN satellite is not provisioned
+            repo.isSatelliteProvisioned.value = false
 
             // THEN icon is null because the device is not provisioned
             assertThat(latest).isNull()
 
-            // GIVEN device becomes provisioned
-            deviceProvisionedRepository.setDeviceProvisioned(true)
+            // GIVEN satellite becomes provisioned
+            repo.isSatelliteProvisioned.value = true
 
             // Wait for delay to be completed
             advanceTimeBy(10.seconds)
@@ -285,8 +279,8 @@
             // GIVEN apm is disabled
             airplaneModeRepository.setIsAirplaneMode(false)
 
-            // GIVEN device is provisioned
-            deviceProvisionedRepository.setDeviceProvisioned(true)
+            // GIVEN satellite is provisioned
+            repo.isSatelliteProvisioned.value = true
 
             // GIVEN wifi network is active
             wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
@@ -474,14 +468,14 @@
             // GIVEN apm is disabled
             airplaneModeRepository.setIsAirplaneMode(false)
 
-            // GIVEN device is not provisioned
-            deviceProvisionedRepository.setDeviceProvisioned(false)
+            // GIVEN satellite is not provisioned
+            repo.isSatelliteProvisioned.value = false
 
             // THEN carrier text is null because the device is not provisioned
             assertThat(latest).isNull()
 
-            // GIVEN device becomes provisioned
-            deviceProvisionedRepository.setDeviceProvisioned(true)
+            // GIVEN satellite becomes provisioned
+            repo.isSatelliteProvisioned.value = true
 
             // Wait for delay to be completed
             advanceTimeBy(10.seconds)
@@ -508,8 +502,8 @@
             // GIVEN apm is disabled
             airplaneModeRepository.setIsAirplaneMode(false)
 
-            // GIVEN device is provisioned
-            deviceProvisionedRepository.setDeviceProvisioned(true)
+            // GIVEN satellite is provisioned
+            repo.isSatelliteProvisioned.value = true
 
             // GIVEN wifi network is active
             wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
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 c9fe449..92de866 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
@@ -29,6 +29,7 @@
 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
 import com.android.systemui.log.assertLogsWtf
@@ -36,9 +37,11 @@
 import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsMediaProjectionChip
+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
@@ -55,12 +58,17 @@
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
-    private val kosmos = Kosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+    private val kosmos =
+        Kosmos().also {
+            it.testCase = this
+            it.testDispatcher = UnconfinedTestDispatcher()
+        }
 
     private val testScope = kosmos.testScope
 
@@ -77,6 +85,11 @@
             kosmos.applicationCoroutineScope,
         )
 
+    @Before
+    fun setUp() {
+        setUpPackageManagerForMediaProjection(kosmos)
+    }
+
     @Test
     fun isTransitioningFromLockscreenToOccluded_started_isTrue() =
         testScope.runTest {
@@ -405,9 +418,9 @@
             assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden)
 
             kosmos.fakeMediaProjectionRepository.mediaProjectionState.value =
-                MediaProjectionState.EntireScreen
+                MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
 
-            assertIsMediaProjectionChip(latest)
+            assertIsShareToAppChip(latest)
         }
 
     private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
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 c3c9907..b66eed0 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,7 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index a1da167..9ab64d65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -14,6 +14,10 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf;
+
+import static com.android.settingslib.flags.Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -21,6 +25,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
@@ -30,7 +35,11 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
-import android.testing.AndroidTestingRunner;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -55,16 +64,31 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 
-@RunWith(AndroidTestingRunner.class)
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
 @RunWithLooper
 @SmallTest
 public class BluetoothControllerImplTest extends SysuiTestCase {
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return allCombinationsOf(FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE);
+    }
+
+    private static final String TEST_EXCLUSIVE_MANAGER = "com.test.manager";
+
+    @Mock
+    private PackageManager mPackageManager;
+
     private UserTracker mUserTracker;
     private LocalBluetoothManager mMockBluetoothManager;
     private CachedBluetoothDeviceManager mMockDeviceManager;
@@ -77,14 +101,21 @@
 
     private FakeExecutor mBackgroundExecutor;
 
+    public BluetoothControllerImplTest(FlagsParameterization flags) {
+        super();
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
         mTestableLooper = TestableLooper.get(this);
         mMockBluetoothManager = mDependency.injectMockDependency(LocalBluetoothManager.class);
         mDevices = new ArrayList<>();
         mUserTracker = mock(UserTracker.class);
         mMockDeviceManager = mock(CachedBluetoothDeviceManager.class);
         mMockAdapter = mock(BluetoothAdapter.class);
+        mContext.setMockPackageManager(mPackageManager);
         when(mMockDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices);
         when(mMockBluetoothManager.getCachedDeviceManager()).thenReturn(mMockDeviceManager);
         mMockLocalAdapter = mock(LocalBluetoothAdapter.class);
@@ -114,6 +145,7 @@
         CachedBluetoothDevice device = mock(CachedBluetoothDevice.class);
         when(device.isConnected()).thenReturn(true);
         when(device.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED);
+        when(device.getDevice()).thenReturn(mock(BluetoothDevice.class));
 
         mDevices.add(device);
         when(mMockLocalAdapter.getConnectionState())
@@ -139,10 +171,12 @@
     public void getConnectedDevices_onlyReturnsConnected() {
         CachedBluetoothDevice device1Disconnected = mock(CachedBluetoothDevice.class);
         when(device1Disconnected.isConnected()).thenReturn(false);
+        when(device1Disconnected.getDevice()).thenReturn(mock(BluetoothDevice.class));
         mDevices.add(device1Disconnected);
 
         CachedBluetoothDevice device2Connected = mock(CachedBluetoothDevice.class);
         when(device2Connected.isConnected()).thenReturn(true);
+        when(device2Connected.getDevice()).thenReturn(mock(BluetoothDevice.class));
         mDevices.add(device2Connected);
 
         mBluetoothControllerImpl.onDeviceAdded(device1Disconnected);
@@ -154,6 +188,46 @@
     }
 
     @Test
+    @EnableFlags(FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+    public void getConnectedDevice_exclusivelyManagedDevice_doNotReturn()
+            throws PackageManager.NameNotFoundException {
+        CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+        when(cachedDevice.isConnected()).thenReturn(true);
+        BluetoothDevice device = mock(BluetoothDevice.class);
+        when(device.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        when(cachedDevice.getDevice()).thenReturn(device);
+        doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
+                TEST_EXCLUSIVE_MANAGER, 0);
+
+        mDevices.add(cachedDevice);
+        mBluetoothControllerImpl.onDeviceAdded(cachedDevice);
+
+        assertThat(mBluetoothControllerImpl.getConnectedDevices()).isEmpty();
+    }
+
+    @Test
+    @DisableFlags(FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
+    public void getConnectedDevice_exclusivelyManagedDevice_returnsConnected()
+            throws PackageManager.NameNotFoundException {
+        CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+        when(cachedDevice.isConnected()).thenReturn(true);
+        BluetoothDevice device = mock(BluetoothDevice.class);
+        when(device.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
+                TEST_EXCLUSIVE_MANAGER.getBytes());
+        when(cachedDevice.getDevice()).thenReturn(device);
+        doReturn(new ApplicationInfo()).when(mPackageManager).getApplicationInfo(
+                TEST_EXCLUSIVE_MANAGER, 0);
+
+        mDevices.add(cachedDevice);
+        mBluetoothControllerImpl.onDeviceAdded(cachedDevice);
+
+        assertThat(mBluetoothControllerImpl.getConnectedDevices()).hasSize(1);
+        assertThat(mBluetoothControllerImpl.getConnectedDevices().get(0))
+                .isEqualTo(cachedDevice);
+    }
+
+    @Test
     public void testOnBluetoothStateChange_updatesBluetoothState() {
         mBluetoothControllerImpl.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
 
@@ -184,6 +258,7 @@
 
         assertFalse(mBluetoothControllerImpl.isBluetoothConnected());
         CachedBluetoothDevice device = mock(CachedBluetoothDevice.class);
+        when(device.getDevice()).thenReturn(mock(BluetoothDevice.class));
         mDevices.add(device);
         when(device.isConnected()).thenReturn(true);
         when(device.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED);
@@ -402,6 +477,7 @@
     private CachedBluetoothDevice createBluetoothDevice(
             int profile, boolean isConnected, boolean isActive) {
         CachedBluetoothDevice device = mock(CachedBluetoothDevice.class);
+        when(device.getDevice()).thenReturn(mock(BluetoothDevice.class));
         mDevices.add(device);
         when(device.isActiveDevice(profile)).thenReturn(isActive);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index 68c1b8d..6894e6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -9,6 +9,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.pm.PackageManager;
 import android.media.MediaRouter;
 import android.media.projection.MediaProjectionInfo;
 import android.media.projection.MediaProjectionManager;
@@ -52,8 +53,12 @@
         mContext.addMockSystemService(MediaRouter.class, mMediaRouter);
         mContext.addMockSystemService(MediaProjectionManager.class, mMediaProjectionManager);
         when(mMediaProjectionManager.getActiveProjectionInfo()).thenReturn(mProjection);
+        when(mProjection.getPackageName()).thenReturn("fake.package");
 
-        mController = new CastControllerImpl(mContext, mock(DumpManager.class));
+        mController = new CastControllerImpl(
+                mContext,
+                mock(PackageManager.class),
+                mock(DumpManager.class));
     }
 
     @Test
@@ -148,16 +153,26 @@
 
     @Test
     public void hasConnectedCastDevice_connected() {
-        CastController.CastDevice castDevice = new CastController.CastDevice();
-        castDevice.state = CastController.CastDevice.STATE_CONNECTED;
+        CastDevice castDevice = new CastDevice(
+                "id",
+                /* name= */ null,
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connected,
+                /* origin= */ CastDevice.CastOrigin.MediaProjection,
+                /* tag= */ null);
         mController.startCasting(castDevice);
         assertTrue(mController.hasConnectedCastDevice());
     }
 
     @Test
     public void hasConnectedCastDevice_notConnected() {
-        CastController.CastDevice castDevice = new CastController.CastDevice();
-        castDevice.state = CastController.CastDevice.STATE_CONNECTING;
+        CastDevice castDevice = new CastDevice(
+                "id",
+                /* name= */ null,
+                /* description= */ null,
+                /* state= */ CastDevice.CastState.Connecting,
+                /* origin= */ CastDevice.CastOrigin.MediaProjection,
+                /* tag= */ null);
         mController.startCasting(castDevice);
         assertTrue(mController.hasConnectedCastDevice());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
new file mode 100644
index 0000000..03ad66c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastDeviceTest.kt
@@ -0,0 +1,441 @@
+/*
+ * 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.policy
+
+import android.Manifest
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.media.MediaRouter
+import android.media.projection.MediaProjectionInfo
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.CastDevice.Companion.toCastDevice
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.Mockito.doAnswer
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class CastDeviceTest : SysuiTestCase() {
+    private val mockAppInfo =
+        mock<ApplicationInfo>().apply { whenever(this.loadLabel(any())).thenReturn("") }
+
+    private val packageManager =
+        mock<PackageManager>().apply {
+            whenever(
+                    this.checkPermission(
+                        Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+                        HEADLESS_REMOTE_PACKAGE,
+                    )
+                )
+                .thenReturn(PackageManager.PERMISSION_GRANTED)
+            whenever(
+                    this.checkPermission(
+                        Manifest.permission.REMOTE_DISPLAY_PROVIDER,
+                        NORMAL_PACKAGE,
+                    )
+                )
+                .thenReturn(PackageManager.PERMISSION_DENIED)
+
+            doAnswer {
+                    // See Utils.isHeadlessRemoteDisplayProvider
+                    if ((it.arguments[0] as Intent).`package` == HEADLESS_REMOTE_PACKAGE) {
+                        emptyList()
+                    } else {
+                        listOf(mock<ResolveInfo>())
+                    }
+                }
+                .whenever(this)
+                .queryIntentActivities(any(), ArgumentMatchers.anyInt())
+
+            whenever(this.getApplicationInfo(any<String>(), any<Int>())).thenReturn(mockAppInfo)
+        }
+
+    @Test
+    fun isCasting_disconnected_false() {
+        val device =
+            CastDevice(
+                id = "id",
+                name = "name",
+                state = CastDevice.CastState.Disconnected,
+                origin = CastDevice.CastOrigin.MediaRouter,
+            )
+
+        assertThat(device.isCasting).isFalse()
+    }
+
+    @Test
+    fun isCasting_connecting_true() {
+        val device =
+            CastDevice(
+                id = "id",
+                name = "name",
+                state = CastDevice.CastState.Connecting,
+                origin = CastDevice.CastOrigin.MediaRouter,
+            )
+
+        assertThat(device.isCasting).isTrue()
+    }
+
+    @Test
+    fun isCasting_connected_true() {
+        val device =
+            CastDevice(
+                id = "id",
+                name = "name",
+                state = CastDevice.CastState.Connected,
+                origin = CastDevice.CastOrigin.MediaRouter,
+            )
+
+        assertThat(device.isCasting).isTrue()
+    }
+
+    @Test
+    fun routeToCastDevice_statusNone_stateDisconnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_NONE)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+    }
+
+    @Test
+    fun routeToCastDevice_statusNotAvailable_stateDisconnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_NOT_AVAILABLE)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+    }
+
+    @Test
+    fun routeToCastDevice_statusScanning_stateDisconnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_SCANNING)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+    }
+
+    @Test
+    fun routeToCastDevice_statusAvailable_isSelectedFalse_stateDisconnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_AVAILABLE)
+                whenever(this.isSelected).thenReturn(false)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+    }
+
+    @Test
+    fun routeToCastDevice_statusAvailable_isSelectedTrue_stateConnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_AVAILABLE)
+                whenever(this.isSelected).thenReturn(true)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+    }
+
+    @Test
+    fun routeToCastDevice_statusInUse_isSelectedFalse_stateDisconnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_IN_USE)
+                whenever(this.isSelected).thenReturn(false)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Disconnected)
+    }
+
+    @Test
+    fun routeToCastDevice_statusInUse_isSelectedTrue_stateConnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_IN_USE)
+                whenever(this.isSelected).thenReturn(true)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+    }
+
+    @Test
+    fun routeToCastDevice_statusConnecting_isSelectedFalse_stateConnecting() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTING)
+                whenever(this.isSelected).thenReturn(false)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Connecting)
+    }
+
+    @Test
+    fun routeToCastDevice_statusConnecting_isSelectedTrue_stateConnecting() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTING)
+                whenever(this.isSelected).thenReturn(true)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Connecting)
+    }
+
+    @Test
+    fun routeToCastDevice_statusConnected_isSelectedFalse_stateConnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTED)
+                whenever(this.isSelected).thenReturn(false)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+    }
+
+    @Test
+    fun routeToCastDevice_statusConnected_isSelectedTrue_stateConnected() {
+        val route =
+            mockRouteInfo().apply {
+                whenever(this.statusCode).thenReturn(MediaRouter.RouteInfo.STATUS_CONNECTED)
+                whenever(this.isSelected).thenReturn(true)
+            }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+    }
+
+    @Test
+    fun routeToCastDevice_tagIsStringType_idMatchesTag() {
+        val route = mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn("FakeTag") }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.id).isEqualTo("FakeTag")
+    }
+
+    @Test
+    fun routeToCastDevice_tagIsOtherType_idMatchesTag() {
+        val tag = listOf("tag1", "tag2")
+        val route = mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn(tag) }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.id).isEqualTo(tag.toString())
+    }
+
+    @Test
+    fun routeToCastDevice_nameMatchesName() {
+        val route = mockRouteInfo().apply { whenever(this.getName(context)).thenReturn("FakeName") }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.name).isEqualTo("FakeName")
+    }
+
+    @Test
+    fun routeToCastDevice_descriptionMatchesDescription() {
+        val route =
+            mockRouteInfo().apply { whenever(this.description).thenReturn("FakeDescription") }
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.description).isEqualTo("FakeDescription")
+    }
+
+    @Test
+    fun routeToCastDevice_tagIsRoute() {
+        val route = mockRouteInfo()
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.tag).isEqualTo(route)
+    }
+
+    @Test
+    fun routeToCastDevice_originIsMediaRouter() {
+        val route = mockRouteInfo()
+
+        val device = route.toCastDevice(context)
+
+        assertThat(device.origin).isEqualTo(CastDevice.CastOrigin.MediaRouter)
+    }
+
+    @Test
+    fun routeToCastDevice_nullValues_ok() {
+        val device = mockRouteInfo().toCastDevice(context)
+
+        assertThat(device.name).isNull()
+        assertThat(device.description).isNull()
+    }
+
+    @Test
+    fun projectionToCastDevice_idMatchesPackage() {
+        val projection =
+            mock<MediaProjectionInfo>().apply {
+                whenever(this.packageName).thenReturn("fake.package")
+            }
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.id).isEqualTo("fake.package")
+    }
+
+    @Test
+    fun projectionToCastDevice_name_packageIsHeadlessRemote_isEmpty() {
+        val projection =
+            mock<MediaProjectionInfo>().apply {
+                whenever(this.packageName).thenReturn(HEADLESS_REMOTE_PACKAGE)
+            }
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.name).isEmpty()
+    }
+
+    @Test
+    fun projectionToCastDevice_name_packageMissingFromPackageManager_isPackageName() {
+        val projection =
+            mock<MediaProjectionInfo>().apply {
+                whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+            }
+
+        whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+            .thenThrow(PackageManager.NameNotFoundException())
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.name).isEqualTo(NORMAL_PACKAGE)
+    }
+
+    @Test
+    fun projectionToCastDevice_name_nameFromPackageManagerEmpty_isPackageName() {
+        val projection =
+            mock<MediaProjectionInfo>().apply {
+                whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+            }
+
+        val appInfo = mock<ApplicationInfo>()
+        whenever(appInfo.loadLabel(packageManager)).thenReturn("")
+        whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+            .thenReturn(appInfo)
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.name).isEqualTo(NORMAL_PACKAGE)
+    }
+
+    @Test
+    fun projectionToCastDevice_name_packageManagerHasName_isName() {
+        val projection =
+            mock<MediaProjectionInfo>().apply {
+                whenever(this.packageName).thenReturn(NORMAL_PACKAGE)
+            }
+
+        val appInfo = mock<ApplicationInfo>()
+        whenever(appInfo.loadLabel(packageManager)).thenReturn("Valid App Name")
+        whenever(packageManager.getApplicationInfo(eq(NORMAL_PACKAGE), any<Int>()))
+            .thenReturn(appInfo)
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.name).isEqualTo("Valid App Name")
+    }
+
+    @Test
+    fun projectionToCastDevice_descriptionIsCasting() {
+        val projection = mockProjectionInfo()
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.description).isEqualTo(context.getString(R.string.quick_settings_casting))
+    }
+
+    @Test
+    fun projectionToCastDevice_stateIsConnected() {
+        val projection = mockProjectionInfo()
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.state).isEqualTo(CastDevice.CastState.Connected)
+    }
+
+    @Test
+    fun projectionToCastDevice_tagIsProjection() {
+        val projection = mockProjectionInfo()
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.tag).isEqualTo(projection)
+    }
+
+    @Test
+    fun projectionToCastDevice_originIsMediaProjection() {
+        val projection = mockProjectionInfo()
+
+        val device = projection.toCastDevice(context, packageManager)
+
+        assertThat(device.origin).isEqualTo(CastDevice.CastOrigin.MediaProjection)
+    }
+
+    private fun mockRouteInfo(): MediaRouter.RouteInfo {
+        return mock<MediaRouter.RouteInfo>().apply { whenever(this.tag).thenReturn(Any()) }
+    }
+
+    private fun mockProjectionInfo(): MediaProjectionInfo {
+        return mock<MediaProjectionInfo>().apply {
+            whenever(this.packageName).thenReturn("fake.package")
+        }
+    }
+
+    private companion object {
+        const val HEADLESS_REMOTE_PACKAGE = "headless.remote.package"
+        const val NORMAL_PACKAGE = "normal.package"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
index 92dd391..eb11e38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -27,12 +27,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Test
@@ -47,16 +41,11 @@
 @TestableLooper.RunWithLooper
 class SettingsProxyTest : SysuiTestCase() {
 
-    private val testDispatcher = StandardTestDispatcher()
-
     private lateinit var mSettings: SettingsProxy
     private lateinit var mContentObserver: ContentObserver
-    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
-        testScope = TestScope(testDispatcher)
-        Dispatchers.setMain(testDispatcher)
         mSettings = FakeSettingsProxy()
         mContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {}
     }
@@ -69,23 +58,6 @@
     }
 
     @Test
-    fun registerContentObserverSuspend_inputString_success() =
-        testScope.runTest {
-            mSettings.registerContentObserver(TEST_SETTING, mContentObserver)
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
-        }
-
-    @Test
-    fun registerContentObserverAsync_inputString_success() {
-        mSettings.registerContentObserverAsync(TEST_SETTING, mContentObserver)
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
-        }
-    }
-
-    @Test
     fun registerContentObserver_inputString_notifyForDescendants_true() {
         mSettings.registerContentObserverSync(
             TEST_SETTING,
@@ -97,31 +69,6 @@
     }
 
     @Test
-    fun registerContentObserverSuspend_inputString_notifyForDescendants_true() =
-        testScope.runTest {
-            mSettings.registerContentObserver(
-                TEST_SETTING,
-                notifyForDescendants = true,
-                mContentObserver
-            )
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
-        }
-
-    @Test
-    fun registerContentObserverAsync_inputString_notifyForDescendants_true() {
-        mSettings.registerContentObserverAsync(
-            TEST_SETTING,
-            notifyForDescendants = true,
-            mContentObserver
-        )
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
-        }
-    }
-
-    @Test
     fun registerContentObserver_inputUri_success() {
         mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver)
         verify(mSettings.getContentResolver())
@@ -129,23 +76,6 @@
     }
 
     @Test
-    fun registerContentObserverSuspend_inputUri_success() =
-        testScope.runTest {
-            mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
-        }
-
-    @Test
-    fun registerContentObserverAsync_inputUri_success() {
-        mSettings.registerContentObserverAsync(TEST_SETTING_URI, mContentObserver)
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
-        }
-    }
-
-    @Test
     fun registerContentObserver_inputUri_notifyForDescendants_true() {
         mSettings.registerContentObserverSync(
             TEST_SETTING_URI,
@@ -157,52 +87,12 @@
     }
 
     @Test
-    fun registerContentObserverSuspend_inputUri_notifyForDescendants_true() =
-        testScope.runTest {
-            mSettings.registerContentObserver(
-                TEST_SETTING_URI,
-                notifyForDescendants = true,
-                mContentObserver
-            )
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
-        }
-
-    @Test
-    fun registerContentObserverAsync_inputUri_notifyForDescendants_true() {
-        mSettings.registerContentObserverAsync(
-            TEST_SETTING_URI,
-            notifyForDescendants = true,
-            mContentObserver
-        )
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver))
-        }
-    }
-
-    @Test
-    fun unregisterContentObserverSync() {
+    fun unregisterContentObserver() {
         mSettings.unregisterContentObserverSync(mContentObserver)
         verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver))
     }
 
     @Test
-    fun unregisterContentObserverSuspend_inputString_success() =
-        testScope.runTest {
-            mSettings.unregisterContentObserver(mContentObserver)
-            verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver))
-        }
-
-    @Test
-    fun unregisterContentObserverAsync_inputString_success() {
-        mSettings.unregisterContentObserverAsync(mContentObserver)
-        testScope.launch {
-            verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver))
-        }
-    }
-
-    @Test
     fun getString_keyPresent_returnValidValue() {
         mSettings.putString(TEST_SETTING, "test")
         assertThat(mSettings.getString(TEST_SETTING)).isEqualTo("test")
@@ -313,15 +203,12 @@
 
         private val mContentResolver = mock(ContentResolver::class.java)
         private val settingToValueMap: MutableMap<String, String> = mutableMapOf()
-        private val testDispatcher = StandardTestDispatcher()
 
         override fun getContentResolver() = mContentResolver
 
         override fun getUriFor(name: String) =
             Uri.parse(StringBuilder().append("content://settings/").append(name).toString())
 
-        override fun getBackgroundDispatcher() = testDispatcher
-
         override fun getString(name: String): String {
             return settingToValueMap[name] ?: ""
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
index 4cb5fa9..38469ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -30,12 +30,6 @@
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserTracker
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
 import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Test
@@ -53,7 +47,6 @@
     private var mUserTracker = FakeUserTracker()
     private var mSettings: UserSettingsProxy = FakeUserSettingsProxy(mUserTracker)
     private var mContentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {}
-    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
@@ -61,9 +54,6 @@
             listOf(UserInfo(MAIN_USER_ID, "main", UserInfo.FLAG_MAIN)),
             selectedUserIndex = 0
         )
-        val testDispatcher = StandardTestDispatcher()
-        testScope = TestScope(testDispatcher)
-        Dispatchers.setMain(testDispatcher)
     }
 
     @Test
@@ -83,41 +73,6 @@
     }
 
     @Test
-    fun registerContentObserverForUserSuspend_inputString_success() =
-        testScope.runTest {
-            mSettings.registerContentObserverForUser(
-                TEST_SETTING,
-                mContentObserver,
-                mUserTracker.userId
-            )
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(false),
-                    eq(mContentObserver),
-                    eq(MAIN_USER_ID)
-                )
-        }
-
-    @Test
-    fun registerContentObserverForUserAsync_inputString_success() {
-        mSettings.registerContentObserverForUserAsync(
-            TEST_SETTING,
-            mContentObserver,
-            mUserTracker.userId
-        )
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(false),
-                    eq(mContentObserver),
-                    eq(MAIN_USER_ID)
-                )
-        }
-    }
-
-    @Test
     fun registerContentObserverForUser_inputString_notifyForDescendants_true() {
         mSettings.registerContentObserverForUserSync(
             TEST_SETTING,
@@ -135,45 +90,6 @@
     }
 
     @Test
-    fun registerContentObserverForUserSuspend_inputString_notifyForDescendants_true() =
-        testScope.runTest {
-            mSettings.registerContentObserverForUser(
-                TEST_SETTING,
-                notifyForDescendants = true,
-                mContentObserver,
-                mUserTracker.userId
-            )
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(
-                        true,
-                    ),
-                    eq(mContentObserver),
-                    eq(MAIN_USER_ID)
-                )
-        }
-
-    @Test
-    fun registerContentObserverForUserAsync_inputString_notifyForDescendants_true() {
-        mSettings.registerContentObserverForUserAsync(
-            TEST_SETTING,
-            notifyForDescendants = true,
-            mContentObserver,
-            mUserTracker.userId
-        )
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(true),
-                    eq(mContentObserver),
-                    eq(MAIN_USER_ID)
-                )
-        }
-    }
-
-    @Test
     fun registerContentObserverForUser_inputUri_success() {
         mSettings.registerContentObserverForUserSync(
             TEST_SETTING_URI,
@@ -190,41 +106,6 @@
     }
 
     @Test
-    fun registerContentObserverForUserSuspend_inputUri_success() =
-        testScope.runTest {
-            mSettings.registerContentObserverForUser(
-                TEST_SETTING_URI,
-                mContentObserver,
-                mUserTracker.userId
-            )
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(false),
-                    eq(mContentObserver),
-                    eq(MAIN_USER_ID)
-                )
-        }
-
-    @Test
-    fun registerContentObserverForUserAsync_inputUri_success() {
-        mSettings.registerContentObserverForUserAsync(
-            TEST_SETTING_URI,
-            mContentObserver,
-            mUserTracker.userId
-        )
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(false),
-                    eq(mContentObserver),
-                    eq(MAIN_USER_ID)
-                )
-        }
-    }
-
-    @Test
     fun registerContentObserverForUser_inputUri_notifyForDescendants_true() {
         mSettings.registerContentObserverForUserSync(
             TEST_SETTING_URI,
@@ -242,45 +123,6 @@
     }
 
     @Test
-    fun registerContentObserverForUserSuspend_inputUri_notifyForDescendants_true() =
-        testScope.runTest {
-            mSettings.registerContentObserverForUser(
-                TEST_SETTING_URI,
-                notifyForDescendants = true,
-                mContentObserver,
-                mUserTracker.userId
-            )
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(
-                        true,
-                    ),
-                    eq(mContentObserver),
-                    eq(MAIN_USER_ID)
-                )
-        }
-
-    @Test
-    fun registerContentObserverForUserAsync_inputUri_notifyForDescendants_true() {
-        mSettings.registerContentObserverForUserAsync(
-            TEST_SETTING_URI,
-            notifyForDescendants = true,
-            mContentObserver,
-            mUserTracker.userId
-        )
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(true),
-                    eq(mContentObserver),
-                    eq(MAIN_USER_ID)
-                )
-        }
-    }
-
-    @Test
     fun registerContentObserver_inputUri_success() {
         mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver)
         verify(mSettings.getContentResolver())
@@ -288,33 +130,6 @@
     }
 
     @Test
-    fun registerContentObserverSuspend_inputUri_success() =
-        testScope.runTest {
-            mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(false),
-                    eq(mContentObserver),
-                    eq(0)
-                )
-        }
-
-    @Test
-    fun registerContentObserverAsync_inputUri_success() {
-        mSettings.registerContentObserverAsync(TEST_SETTING_URI, mContentObserver)
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(false),
-                    eq(mContentObserver),
-                    eq(0)
-                )
-        }
-    }
-
-    @Test
     fun registerContentObserver_inputUri_notifyForDescendants_true() {
         mSettings.registerContentObserverSync(
             TEST_SETTING_URI,
@@ -326,33 +141,6 @@
     }
 
     @Test
-    fun registerContentObserverSuspend_inputUri_notifyForDescendants_true() =
-        testScope.runTest {
-            mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(false),
-                    eq(mContentObserver),
-                    eq(0)
-                )
-        }
-
-    @Test
-    fun registerContentObserverAsync_inputUri_notifyForDescendants_true() {
-        mSettings.registerContentObserverAsync(TEST_SETTING_URI, mContentObserver)
-        testScope.launch {
-            verify(mSettings.getContentResolver())
-                .registerContentObserver(
-                    eq(TEST_SETTING_URI),
-                    eq(false),
-                    eq(mContentObserver),
-                    eq(0)
-                )
-        }
-    }
-
-    @Test
     fun getString_keyPresent_returnValidValue() {
         mSettings.putString(TEST_SETTING, "test")
         assertThat(mSettings.getString(TEST_SETTING)).isEqualTo("test")
@@ -517,15 +305,12 @@
         private val mContentResolver = mock(ContentResolver::class.java)
         private val userIdToSettingsValueMap: MutableMap<Int, MutableMap<String, String>> =
             mutableMapOf()
-        private val testDispatcher = StandardTestDispatcher()
 
         override fun getContentResolver() = mContentResolver
 
         override fun getUriFor(name: String) =
             Uri.parse(StringBuilder().append(URI_PREFIX).append(name).toString())
 
-        override fun getBackgroundDispatcher() = testDispatcher
-
         override fun getStringForUser(name: String, userHandle: Int) =
             userIdToSettingsValueMap[userHandle]?.get(name) ?: ""
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 05d07e5..6efb7d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
@@ -86,7 +87,9 @@
 import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.volume.domain.interactor.VolumeDialogInteractor;
 import com.android.systemui.volume.domain.interactor.VolumePanelNavigationInteractor;
+import com.android.systemui.volume.panel.shared.flag.VolumePanelFlag;
 import com.android.systemui.volume.ui.navigation.VolumeNavigator;
 
 import dagger.Lazy;
@@ -118,7 +121,6 @@
     View mDrawerNormal;
     ViewGroup mDialogRowsView;
     CaptionsToggleImageButton mODICaptionsIcon;
-
     private TestableLooper mTestableLooper;
     private ConfigurationController mConfigurationController;
     private int mOriginalOrientation;
@@ -148,6 +150,10 @@
     private VolumePanelNavigationInteractor mVolumePanelNavigationInteractor;
     @Mock
     private VolumeNavigator mVolumeNavigator;
+    @Mock
+    private VolumePanelFlag mVolumePanelFlag;
+    @Mock
+    private VolumeDialogInteractor mVolumeDialogInteractor;
 
     private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
             new CsdWarningDialog.Factory() {
@@ -208,10 +214,12 @@
                 mCsdWarningDialogFactory,
                 mPostureController,
                 mTestableLooper.getLooper(),
+                mVolumePanelFlag,
                 mDumpManager,
                 mLazySecureSettings,
                 mVibratorHelper,
-                new FakeSystemClock());
+                new FakeSystemClock(),
+                mVolumeDialogInteractor);
         mDialog.init(0, null);
         State state = createShellState();
         mDialog.onStateChangedH(state);
@@ -771,6 +779,15 @@
         assertFalse(foundDnDIcon);
     }
 
+    @Test
+    public void testInteractor_onShow() {
+        mDialog.show(SHOW_REASON_UNKNOWN);
+        mTestableLooper.processAllMessages();
+
+        verify(mVolumeDialogInteractor, atLeastOnce()).onDialogShown();
+        verify(mVolumeDialogInteractor, atLeastOnce()).onDialogDismissed(); // dismiss by timeout
+    }
+
     /**
      * @return true if at least one volume row has the DND icon
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
new file mode 100644
index 0000000..dcac85e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogRepositoryTest : SysuiTestCase() {
+    private lateinit var underTest: VolumeDialogRepository
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    @Before
+    fun setUp() {
+        underTest = kosmos.volumeDialogRepository
+    }
+
+    @Test
+    fun isDialogVisible_initialValueFalse() {
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isDialogVisible)
+            runCurrent()
+
+            assertThat(isVisible).isFalse()
+        }
+    }
+
+    @Test
+    fun isDialogVisible_onChange() {
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isDialogVisible)
+            runCurrent()
+
+            underTest.setDialogVisibility(true)
+            assertThat(isVisible).isTrue()
+
+            underTest.setDialogVisibility(false)
+            assertThat(isVisible).isFalse()
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
new file mode 100644
index 0000000..5c735e3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.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.volume.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogInteractorTest : SysuiTestCase() {
+    private lateinit var underTest: VolumeDialogInteractor
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    @Before
+    fun setUp() {
+        underTest = kosmos.volumeDialogInteractor
+    }
+
+    @Test
+    fun onDialogDismissed() {
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isDialogVisible)
+            underTest.onDialogDismissed()
+            runCurrent()
+
+            assertThat(isVisible).isFalse()
+        }
+    }
+
+    @Test
+    fun onDialogShown() {
+        testScope.runTest {
+            val isVisible by collectLastValue(underTest.isDialogVisible)
+            underTest.onDialogShown()
+            runCurrent()
+
+            assertThat(isVisible).isTrue()
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 0f89dcc..fabb9b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -169,6 +169,7 @@
 import com.android.wm.shell.bubbles.BubbleViewProvider;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.bubbles.StackEducationView;
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
 import com.android.wm.shell.bubbles.properties.BubbleProperties;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -2109,6 +2110,33 @@
     }
 
     @Test
+    public void registerBubbleBarListener_switchToBarWhileExpanded() {
+        mBubbleProperties.mIsBubbleBarEnabled = true;
+        mPositioner.setIsLargeScreen(true);
+
+        mEntryListener.onEntryAdded(mRow);
+        mBubbleController.updateBubble(mBubbleEntry);
+        BubbleStackView stackView = mBubbleController.getStackView();
+        spyOn(stackView);
+
+        mBubbleData.setExpanded(true);
+
+        assertStackMode();
+        assertThat(mBubbleData.isExpanded()).isTrue();
+        assertThat(stackView.isExpanded()).isTrue();
+
+        FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+        mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+        BubbleBarLayerView layerView = mBubbleController.getLayerView();
+        spyOn(layerView);
+
+        assertBarMode();
+        assertThat(mBubbleData.isExpanded()).isTrue();
+        assertThat(layerView.isExpanded()).isTrue();
+    }
+
+    @Test
     public void switchBetweenBarAndStack_noBubbles_shouldBeIgnored() {
         mBubbleProperties.mIsBubbleBarEnabled = false;
         mPositioner.setIsLargeScreen(true);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUnlockAnimationControllerKosmos.kt
similarity index 76%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUnlockAnimationControllerKosmos.kt
index 6085a1f..1fa6813 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUnlockAnimationControllerKosmos.kt
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.keyguard
 
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.util.mockito.mock
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.keyguardUnlockAnimationController by
+    Kosmos.Fixture { mock<KeyguardUnlockAnimationController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
index 5a092f3..976a19c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
@@ -16,16 +16,19 @@
 
 package com.android.systemui.animation
 
+import android.content.applicationContext
 import com.android.systemui.jank.interactionJankMonitor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testCase
+import com.android.systemui.util.mockito.mock
 
 val Kosmos.dialogTransitionAnimator by Fixture {
     fakeDialogTransitionAnimator(
         // The main thread is checked in a bunch of places inside the different transitions
         // animators, so we have to pass the real main executor here.
-        mainExecutor = testCase.context.mainExecutor,
+        mainExecutor = applicationContext.mainExecutor,
         interactionJankMonitor = interactionJankMonitor,
     )
 }
+
+val Kosmos.mockDialogTransitionAnimator by Fixture { mock<DialogTransitionAnimator>() }
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 8281984..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
@@ -22,7 +22,6 @@
 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.fpsUnlockTracker
 import com.android.systemui.keyguard.domain.interactor.deviceEntrySideFpsOverlayInteractor
 import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
 import com.android.systemui.kosmos.Kosmos
@@ -38,7 +37,6 @@
         { biometricStatusInteractor },
         { displayStateInteractor },
         { deviceEntrySideFpsOverlayInteractor },
-        { fpsUnlockTracker },
         { layoutInflater },
         { sideFpsProgressBarViewModel },
         { sideFpsSensorInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
index a7bf87d..d280be2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
@@ -6,6 +6,7 @@
 import com.android.systemui.communal.shared.model.CommunalScenes
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -13,20 +14,25 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /** Fake implementation of [CommunalSceneRepository]. */
 @OptIn(ExperimentalCoroutinesApi::class)
 class FakeCommunalSceneRepository(
-    applicationScope: CoroutineScope,
+    private val applicationScope: CoroutineScope,
     override val currentScene: MutableStateFlow<SceneKey> =
         MutableStateFlow(CommunalScenes.Default),
 ) : CommunalSceneRepository {
-    override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) =
-        snapToScene(toScene)
 
-    override fun snapToScene(toScene: SceneKey) {
-        this.currentScene.value = toScene
-        this._transitionState.value = flowOf(ObservableTransitionState.Idle(toScene))
+    override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) =
+        snapToScene(toScene, 0)
+
+    override fun snapToScene(toScene: SceneKey, delayMillis: Long) {
+        applicationScope.launch {
+            delay(delayMillis)
+            currentScene.value = toScene
+            _transitionState.value = flowOf(ObservableTransitionState.Idle(toScene))
+        }
     }
 
     private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
index e3c218d..1ae8449 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal.ui.viewmodel
 
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.util.communalColors
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.ui.viewmodel.dreamingToGlanceableHubTransitionViewModel
@@ -24,12 +25,14 @@
 import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @OptIn(ExperimentalCoroutinesApi::class)
 val Kosmos.communalTransitionViewModel by
     Kosmos.Fixture {
         CommunalTransitionViewModel(
+            applicationScope = applicationCoroutineScope,
             glanceableHubToLockscreenTransitionViewModel =
                 glanceableHubToLockscreenTransitionViewModel,
             lockscreenToGlanceableHubTransitionViewModel =
@@ -37,6 +40,7 @@
             dreamToGlanceableHubTransitionViewModel = dreamingToGlanceableHubTransitionViewModel,
             glanceableHubToDreamTransitionViewModel = glanceableHubToDreamingTransitionViewModel,
             communalInteractor = communalInteractor,
+            communalSceneInteractor = communalSceneInteractor,
             keyguardTransitionInteractor = keyguardTransitionInteractor,
             communalColors = communalColors,
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryKosmos.kt
new file mode 100644
index 0000000..e4efadbd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/data/repository/DeviceConfigRepositoryKosmos.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.deviceconfig.data.repository
+
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.util.deviceConfigProxy
+
+val Kosmos.deviceConfigRepository by Fixture {
+    DeviceConfigRepository(
+        backgroundExecutor = fakeExecutor,
+        backgroundDispatcher = testDispatcher,
+        dataSource = deviceConfigProxy,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorKosmos.kt
index 6085a1f..d538497 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceconfig/domain/interactor/DeviceConfigInteractorKosmos.kt
@@ -14,9 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.deviceconfig.domain.interactor
 
+import com.android.systemui.deviceconfig.data.repository.deviceConfigRepository
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.Kosmos.Fixture
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.deviceConfigInteractor by Fixture {
+    DeviceConfigInteractor(
+        repository = deviceConfigRepository,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
index 24603ef..eff99e04 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/qs/QSLongPressEffectKosmos.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.haptics.qs
 
 import com.android.systemui.haptics.vibratorHelper
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.keyguardStateController
 
 val Kosmos.qsLongPressEffect by
-    Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardInteractor) }
+    Kosmos.Fixture { QSLongPressEffect(vibratorHelper, keyguardStateController) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
index 772ce41..e6e7b87 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
@@ -32,6 +32,13 @@
         repo.start()
     }
 
+    fun hideThroughCloseSystemDialogs() {
+        fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+            context,
+            Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+        )
+    }
+
     fun hideFromActivity() {
         fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
             context,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 22b8c8db..dca531a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -80,7 +80,7 @@
     override val isAodAvailable: StateFlow<Boolean> = _isAodAvailable
 
     private val _isDreaming = MutableStateFlow(false)
-    override val isDreaming: Flow<Boolean> = _isDreaming
+    override val isDreaming: MutableStateFlow<Boolean> = _isDreaming
 
     private val _isDreamingWithOverlay = MutableStateFlow(false)
     override val isDreamingWithOverlay: Flow<Boolean> = _isDreamingWithOverlay
@@ -119,6 +119,8 @@
     private val _keyguardAlpha = MutableStateFlow(1f)
     override val keyguardAlpha: StateFlow<Float> = _keyguardAlpha
 
+    override val panelAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
+
     override val lastRootViewTapPosition: MutableStateFlow<Point?> = MutableStateFlow(null)
 
     override val ambientIndicationVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
@@ -204,7 +206,7 @@
         _isAodAvailable.value = value
     }
 
-    fun setDreaming(isDreaming: Boolean) {
+    override fun setDreaming(isDreaming: Boolean) {
         _isDreaming.value = isDreaming
     }
 
@@ -259,6 +261,10 @@
         _keyguardAlpha.value = alpha
     }
 
+    override fun setPanelAlpha(alpha: Float) {
+        panelAlpha.value = alpha
+    }
+
     fun setIsEncryptedOrLockdown(value: Boolean) {
         _isEncryptedOrLockdown.value = value
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index 162fd90..28bd439 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -34,7 +33,6 @@
             bgDispatcher = testDispatcher,
             mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
-            flags = featureFlagsClassic,
             shadeRepository = shadeRepository,
             powerInteractor = powerInteractor,
             glanceableHubTransitions = glanceableHubTransitions,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
index 98babff..d72b9c1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt
@@ -18,7 +18,6 @@
 
 import com.android.keyguard.keyguardSecurityModel
 import com.android.systemui.communal.domain.interactor.communalInteractor
-import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -37,7 +36,6 @@
             mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
             communalInteractor = communalInteractor,
-            flags = featureFlagsClassic,
             keyguardSecurityModel = keyguardSecurityModel,
             selectedUserInteractor = selectedUserInteractor,
             powerInteractor = powerInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index 02842cc..b5ca964 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.domain.interactor.PowerInteractorFactory
 import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -34,6 +35,7 @@
 import com.android.systemui.util.mockito.whenever
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.TestScope
 
 /**
@@ -60,9 +62,11 @@
     ): WithDependencies {
         // Mock these until they are replaced by kosmos
         val currentKeyguardStateFlow = MutableSharedFlow<KeyguardState>()
+        val transitionStateFlow = MutableStateFlow(TransitionStep())
         val keyguardTransitionInteractor =
             mock<KeyguardTransitionInteractor>().also {
                 whenever(it.currentKeyguardState).thenReturn(currentKeyguardStateFlow)
+                whenever(it.transitionState).thenReturn(transitionStateFlow)
             }
         val configurationDimensionFlow = MutableSharedFlow<ConfigurationBasedDimensions>()
         configurationDimensionFlow.tryEmit(
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 6f168d4..6cf668c 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
@@ -33,6 +33,7 @@
         keyguardInteractor = keyguardInteractor,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+        lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
         aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
         occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
         keyguardClockViewModel = keyguardClockViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
index 19e4241..8549a30 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.domain.interactor.powerInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 var Kosmos.goneToAodTransitionViewModel by Fixture {
     GoneToAodTransitionViewModel(
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+        powerInteractor = powerInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
index 6d46694..3c62b44 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+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.ui.systemBarUtilsProxy
 
@@ -30,6 +31,7 @@
         KeyguardClockViewModel(
             keyguardClockInteractor = keyguardClockInteractor,
             applicationScope = applicationCoroutineScope,
+            aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel,
             notifsKeyguardInteractor = notificationsKeyguardInteractor,
             shadeInteractor = shadeInteractor,
             systemBarUtils = systemBarUtilsProxy,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
index 07b4cd4..f45e33b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelKosmos.kt
@@ -22,11 +22,13 @@
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.power.domain.interactor.powerInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-val Kosmos.lockscreenToAodTransitionViewModel by Fixture {
+var Kosmos.lockscreenToAodTransitionViewModel by Fixture {
     LockscreenToAodTransitionViewModel(
         deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
+        powerInteractor = powerInteractor,
         shadeDependentFlows = shadeDependentFlows,
         animationFlow = keyguardTransitionAnimationFlow,
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 6d2d04a..31cdbc7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -62,9 +63,12 @@
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.statusbar.phone.scrimController
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.fakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.wifiInteractor
 import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
 import com.android.systemui.util.time.systemClock
+import com.android.systemui.volume.domain.interactor.volumeDialogInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /**
@@ -111,6 +115,7 @@
     val deviceEntryUdfpsInteractor by lazy { kosmos.deviceEntryUdfpsInteractor }
     val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
     val communalInteractor by lazy { kosmos.communalInteractor }
+    val communalSceneInteractor by lazy { kosmos.communalSceneInteractor }
     val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin }
     val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor }
     val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository }
@@ -130,6 +135,9 @@
     val shadeController by lazy { kosmos.shadeController }
     val shadeRepository by lazy { kosmos.shadeRepository }
     val shadeInteractor by lazy { kosmos.shadeInteractor }
+    val wifiInteractor by lazy { kosmos.wifiInteractor }
+    val fakeWifiRepository by lazy { kosmos.fakeWifiRepository }
+    val volumeDialogInteractor by lazy { kosmos.volumeDialogInteractor }
 
     val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
     val scrimController by lazy { kosmos.scrimController }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
index d56222e..b8b0060 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
@@ -45,5 +45,6 @@
             logger = mediaUiEventLogger,
             mediaFlags = mediaFlags,
             mediaFilterRepository = mediaFilterRepository,
+            mediaLoadingLogger = mediaLoadingLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaLoadingLoggerKosmos.kt
similarity index 65%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaLoadingLoggerKosmos.kt
index 6085a1f..96886f7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaLoadingLoggerKosmos.kt
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.media.controls.domain.pipeline
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.log.logcatLogBuffer
+import org.mockito.Mockito.mock
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.mediaLoadingLogger by
+    Kosmos.Fixture { MediaLoadingLogger(logcatLogBuffer("MediaLoadingLogBuffer")) }
+val Kosmos.mockMediaLoadingLogger by Kosmos.Fixture { mock(MediaLoadingLogger::class.java) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
index e5e2aff..ca1b3f5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractorKosmos.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.media.controls.data.repository.mediaDataRepository
 import com.android.systemui.media.controls.data.repository.mediaFilterRepository
 import com.android.systemui.media.controls.domain.pipeline.mediaDataCombineLatest
 import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
@@ -33,7 +32,6 @@
     Kosmos.Fixture {
         MediaCarouselInteractor(
             applicationScope = applicationCoroutineScope,
-            mediaDataRepository = mediaDataRepository,
             mediaDataProcessor = mediaDataProcessor,
             mediaTimeoutListener = mediaTimeoutListener,
             mediaResumeListener = mediaResumeListener,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
index c4365c9..d631f92 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
@@ -25,4 +25,10 @@
 
     override val mediaProjectionState: MutableStateFlow<MediaProjectionState> =
         MutableStateFlow(MediaProjectionState.NotProjecting)
+
+    var stopProjectingInvoked = false
+
+    override suspend fun stopProjecting() {
+        stopProjectingInvoked = true
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigation/data/repository/NavigationRepositoryKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/navigation/data/repository/NavigationRepositoryKosmos.kt
index 6085a1f..717a300 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigation/data/repository/NavigationRepositoryKosmos.kt
@@ -14,9 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.navigation.data.repository
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.navigationbar.navigationModeController
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.navigationRepository by Fixture {
+    NavigationRepository(
+        controller = navigationModeController,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorKosmos.kt
index 6085a1f..80fe3b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigation/domain/interactor/NavigationInteractorKosmos.kt
@@ -14,9 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.navigation.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.navigation.data.repository.navigationRepository
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.navigationInteractor by Fixture {
+    NavigationInteractor(
+        repository = navigationRepository,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationModeControllerKosmos.kt
similarity index 79%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationModeControllerKosmos.kt
index 6085a1f..f2fb1a8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/NavigationModeControllerKosmos.kt
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.navigationbar
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.util.mockito.mock
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.navigationModeController by Fixture { mock<NavigationModeController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt
new file mode 100644
index 0000000..cc7eb6b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.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.panels.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import kotlinx.coroutines.flow.Flow
+
+class FakeTileAvailabilityInteractor(
+        private val availabilityFlows: Map<Int, Flow<Boolean>>
+) : QSTileAvailabilityInteractor {
+    override fun availability(user: UserHandle): Flow<Boolean> {
+        return availabilityFlows.getValue(user.identifier)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt
new file mode 100644
index 0000000..40e6c75
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.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.qs.panels.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.user.data.repository.userRepository
+
+var Kosmos.tileAvailabilityInteractorsMap by Kosmos.Fixture {
+    emptyMap<String, QSTileAvailabilityInteractor>()
+}
+
+val Kosmos.newTilesAvailabilityInteractor by Kosmos.Fixture {
+    NewTilesAvailabilityInteractor(
+            tileAvailabilityInteractorsMap,
+            userRepository,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorKosmos.kt
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorKosmos.kt
index 6085a1f..95fd10e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorKosmos.kt
@@ -14,9 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.qs.panels.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
+import com.android.systemui.qs.qsTileFactory
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.tilesAvailabilityInteractor by Kosmos.Fixture {
+    TilesAvailabilityInteractor(
+            newTilesAvailabilityInteractor,
+            qsTileFactory,
+            QSPipelineFlagsRepository(),
+    )
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
index 612a5d9..b03542c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelKosmos.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap
 import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor
 import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
+import com.android.systemui.qs.panels.domain.interactor.tilesAvailabilityInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.minimumTilesInteractor
 
@@ -30,6 +31,7 @@
         EditModeViewModel(
             editTilesListInteractor,
             currentTilesInteractor,
+            tilesAvailabilityInteractor,
             minimumTilesInteractor,
             infiniteGridLayout,
             applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt
index d82286f..cf18c0e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testScope
@@ -67,5 +68,6 @@
         uiEventLogger = uiEventLogger,
         sceneBackInteractor = sceneBackInteractor,
         shadeSessionStorage = shadeSessionStorage,
+        windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index ef7aa63..066736c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.domain.resolver.sceneFamilyResolvers
 import com.android.systemui.scene.shared.logger.sceneLogger
 
 val Kosmos.sceneInteractor by
@@ -28,6 +29,7 @@
             applicationScope = applicationCoroutineScope,
             repository = sceneContainerRepository,
             logger = sceneLogger,
+            sceneFamilyResolvers = { sceneFamilyResolvers },
             deviceUnlockedInteractor = deviceUnlockedInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
new file mode 100644
index 0000000..6be1939
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/resolver/SceneResolverKosmos.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.domain.resolver
+
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.sceneFamilyResolvers: Map<SceneKey, SceneResolver>
+    get() =
+        mapOf(
+            SceneFamilies.Home to homeSceneFamilyResolver,
+            SceneFamilies.NotifShade to notifShadeSceneFamilyResolver,
+            SceneFamilies.QuickSettings to quickSettingsSceneFamilyResolver,
+        )
+
+val Kosmos.homeSceneFamilyResolver by
+    Kosmos.Fixture {
+        HomeSceneFamilyResolver(
+            applicationScope = applicationCoroutineScope,
+            deviceEntryInteractor = deviceEntryInteractor,
+        )
+    }
+
+val Kosmos.notifShadeSceneFamilyResolver by
+    Kosmos.Fixture {
+        NotifShadeSceneFamilyResolver(
+            applicationScope = applicationCoroutineScope,
+            shadeInteractor = shadeInteractor,
+        )
+    }
+
+val Kosmos.quickSettingsSceneFamilyResolver by
+    Kosmos.Fixture {
+        QuickSettingsSceneFamilyResolver(
+            applicationScope = applicationCoroutineScope,
+            shadeInteractor = shadeInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
index fb0e368..30b4763 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt
@@ -22,4 +22,10 @@
 class FakeScreenRecordRepository : ScreenRecordRepository {
     override val screenRecordState: MutableStateFlow<ScreenRecordModel> =
         MutableStateFlow(ScreenRecordModel.DoingNothing)
+
+    var stopRecordingInvoked = false
+
+    override suspend fun stopRecording() {
+        stopRecordingInvoked = true
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
index d08855f..0bc4d54 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt
@@ -20,12 +20,9 @@
 
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.concurrency.fakeExecutor
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.log.LogBuffer
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -52,15 +49,13 @@
             shadeInteractor = shadeInteractor,
             sceneInteractor = sceneInteractor,
             notificationStackScrollLayout = mock<NotificationStackScrollLayout>(),
-            deviceEntryInteractor = deviceEntryInteractor,
-            touchLog = mock<LogBuffer>(),
             vibratorHelper = mock<VibratorHelper>(),
             commandQueue = mock<CommandQueue>(),
             statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>(),
             notificationShadeWindowController = mock<NotificationShadeWindowController>(),
-            assistManagerLazy = { mock<AssistManager>() },
-            deviceUnlockedInteractor = deviceUnlockedInteractor,
-        )
+        ) {
+            mock<AssistManager>()
+        }
     }
 
 val Kosmos.shadeControllerImpl by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
index 0a3a2ee..00b788f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.util.mockito.mock
@@ -25,7 +26,7 @@
 val Kosmos.shadeLockscreenInteractor by
     Kosmos.Fixture {
         ShadeLockscreenInteractorImpl(
-            applicationScope = applicationCoroutineScope,
+            mainDispatcher = testDispatcher,
             backgroundScope = applicationCoroutineScope,
             shadeInteractor = shadeInteractorImpl,
             sceneInteractor = sceneInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
index 872eba06..1ca3509 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneViewModelKosmos.kt
@@ -17,13 +17,7 @@
 package com.android.systemui.shade.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneViewModel
 
 val Kosmos.notificationsShadeSceneViewModel: NotificationsShadeSceneViewModel by
-    Kosmos.Fixture {
-        NotificationsShadeSceneViewModel(
-            applicationScope = applicationCoroutineScope,
-            overlayShadeViewModel = overlayShadeViewModel,
-        )
-    }
+    Kosmos.Fixture { NotificationsShadeSceneViewModel() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
index 45ec032..fec1028 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
@@ -14,21 +14,16 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.shade.ui.viewmodel
 
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 val Kosmos.overlayShadeViewModel: OverlayShadeViewModel by
     Kosmos.Fixture {
         OverlayShadeViewModel(
             applicationScope = applicationCoroutineScope,
             sceneInteractor = sceneInteractor,
-            deviceEntryInteractor = deviceEntryInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
index c5625e4..4d81ea1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModel
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
 import com.android.systemui.qs.ui.adapter.qsSceneAdapter
@@ -27,7 +26,6 @@
 val Kosmos.quickSettingsShadeSceneViewModel: QuickSettingsShadeSceneViewModel by
     Kosmos.Fixture {
         QuickSettingsShadeSceneViewModel(
-            applicationScope = applicationCoroutineScope,
             overlayShadeViewModel = overlayShadeViewModel,
             brightnessSliderViewModel = brightnessSliderViewModel,
             tileGridViewModel = tileGridViewModel,
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 8d653f7..0e21698 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
@@ -21,6 +21,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 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
@@ -33,6 +34,7 @@
             applicationScope = applicationCoroutineScope,
             context = applicationContext,
             activityStarter = activityStarter,
+            sceneInteractor = sceneInteractor,
             shadeInteractor = shadeInteractor,
             mobileIconsInteractor = mobileIconsInteractor,
             mobileIconsViewModel = mobileIconsViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
similarity index 69%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
index 6085a1f..064e57b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorKosmos.kt
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.statusbar.chips.call.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.callChipInteractor: CallChipInteractor by
+    Kosmos.Fixture { CallChipInteractor(repository = ongoingCallRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelKosmos.kt
new file mode 100644
index 0000000..ab71b5e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelKosmos.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.statusbar.chips.call.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.statusbar.chips.call.domain.interactor.callChipInteractor
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.callChipViewModel: CallChipViewModel by
+    Kosmos.Fixture {
+        CallChipViewModel(
+            scope = applicationCoroutineScope,
+            interactor = callChipInteractor,
+            systemClock = fakeSystemClock,
+            activityStarter = activityStarter,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelKosmos.kt
new file mode 100644
index 0000000..4baa8d0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelKosmos.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.statusbar.chips.casttootherdevice.ui.viewmodel
+
+import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel by
+    Kosmos.Fixture {
+        CastToOtherDeviceChipViewModel(
+            scope = applicationCoroutineScope,
+            mediaProjectionChipInteractor = mediaProjectionChipInteractor,
+            systemClock = fakeSystemClock,
+            endMediaProjectionDialogHelper = endMediaProjectionDialogHelper,
+            dialogTransitionAnimator = mockDialogTransitionAnimator,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt
new file mode 100644
index 0000000..6812a9d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.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.statusbar.chips.mediaprojection.domain.interactor
+
+import android.content.packageManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+
+val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by
+    Kosmos.Fixture {
+        MediaProjectionChipInteractor(
+            scope = applicationCoroutineScope,
+            mediaProjectionRepository = fakeMediaProjectionRepository,
+            packageManager = packageManager,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.kt
new file mode 100644
index 0000000..4f82662
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelperKosmos.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.statusbar.chips.mediaprojection.ui.view
+
+import android.content.applicationContext
+import android.content.packageManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
+
+val Kosmos.endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper by
+    Kosmos.Fixture {
+        EndMediaProjectionDialogHelper(
+            dialogFactory = mockSystemUIDialogFactory,
+            packageManager = packageManager,
+            context = applicationContext,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorKosmos.kt
new file mode 100644
index 0000000..b4e9c14
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorKosmos.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.statusbar.chips.screenrecord.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+
+val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by
+    Kosmos.Fixture {
+        ScreenRecordChipInteractor(
+            scope = applicationCoroutineScope,
+            screenRecordRepository = screenRecordRepository,
+            mediaProjectionRepository = fakeMediaProjectionRepository,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelKosmos.kt
new file mode 100644
index 0000000..99b7ec9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelKosmos.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.statusbar.chips.screenrecord.ui.viewmodel
+
+import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
+import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.screenRecordChipViewModel: ScreenRecordChipViewModel by
+    Kosmos.Fixture {
+        ScreenRecordChipViewModel(
+            scope = applicationCoroutineScope,
+            interactor = screenRecordChipInteractor,
+            endMediaProjectionDialogHelper = endMediaProjectionDialogHelper,
+            dialogTransitionAnimator = mockDialogTransitionAnimator,
+            systemClock = fakeSystemClock,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelKosmos.kt
new file mode 100644
index 0000000..535f81a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelKosmos.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.statusbar.chips.sharetoapp.ui.viewmodel
+
+import com.android.systemui.animation.mockDialogTransitionAnimator
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.shareToAppChipViewModel: ShareToAppChipViewModel by
+    Kosmos.Fixture {
+        ShareToAppChipViewModel(
+            scope = applicationCoroutineScope,
+            mediaProjectionChipInteractor = mediaProjectionChipInteractor,
+            systemClock = fakeSystemClock,
+            endMediaProjectionDialogHelper = endMediaProjectionDialogHelper,
+            dialogTransitionAnimator = mockDialogTransitionAnimator,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/FakeOngoingActivityChipInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/FakeOngoingActivityChipInteractor.kt
deleted file mode 100644
index 90d459b..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/FakeOngoingActivityChipInteractor.kt
+++ /dev/null
@@ -1,26 +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.statusbar.chips.ui.viewmodel
-
-import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
-import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
-import kotlinx.coroutines.flow.MutableStateFlow
-
-class FakeCallChipInteractor : CallChipInteractor() {
-    override val chip: MutableStateFlow<OngoingActivityChipModel> =
-        MutableStateFlow(OngoingActivityChipModel.Hidden)
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index 88bde2e..078e845 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -17,40 +17,19 @@
 package com.android.systemui.statusbar.chips.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
-import com.android.systemui.screenrecord.data.repository.screenRecordRepository
-import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
-import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
-import com.android.systemui.util.time.fakeSystemClock
-
-val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by
-    Kosmos.Fixture {
-        ScreenRecordChipInteractor(
-            scope = applicationCoroutineScope,
-            screenRecordRepository = screenRecordRepository,
-            systemClock = fakeSystemClock,
-        )
-    }
-
-val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by
-    Kosmos.Fixture {
-        MediaProjectionChipInteractor(
-            scope = applicationCoroutineScope,
-            mediaProjectionRepository = fakeMediaProjectionRepository,
-            systemClock = fakeSystemClock,
-        )
-    }
-
-val Kosmos.callChipInteractor: FakeCallChipInteractor by Kosmos.Fixture { FakeCallChipInteractor() }
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel
+import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel
+import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
 
 val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by
     Kosmos.Fixture {
         OngoingActivityChipsViewModel(
             testScope.backgroundScope,
-            screenRecordChipInteractor = screenRecordChipInteractor,
-            mediaProjectionChipInteractor = mediaProjectionChipInteractor,
-            callChipInteractor = callChipInteractor,
+            screenRecordChipViewModel = screenRecordChipViewModel,
+            shareToAppChipViewModel = shareToAppChipViewModel,
+            castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel,
+            callChipViewModel = callChipViewModel,
         )
     }
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 d00eedf..ffd8aab 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
@@ -31,6 +32,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.goneToDozingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.goneToDreamingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.goneToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.lockscreenToDreamingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGoneTransitionViewModel
@@ -70,6 +72,7 @@
         goneToAodTransitionViewModel = goneToAodTransitionViewModel,
         goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
         goneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel,
+        goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel,
         glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
         lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel,
         lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel,
@@ -84,6 +87,7 @@
         primaryBouncerToLockscreenTransitionViewModel =
             primaryBouncerToLockscreenTransitionViewModel,
         aodBurnInViewModel = aodBurnInViewModel,
+        communalSceneInteractor = communalSceneInteractor,
         unfoldTransitionInteractor = unfoldTransitionInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
index 3bb9580..1851c89 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt
@@ -21,8 +21,9 @@
 import com.android.systemui.broadcast.broadcastDispatcher
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.model.sysUiState
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.systemUIDialogFactory by
+val Kosmos.systemUIDialogFactory: SystemUIDialogFactory by
     Kosmos.Fixture {
         SystemUIDialogFactory(
             applicationContext,
@@ -32,3 +33,6 @@
             dialogTransitionAnimator,
         )
     }
+
+val Kosmos.mockSystemUIDialogFactory: SystemUIDialog.Factory by
+    Kosmos.Fixture { mock<SystemUIDialog.Factory>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryKosmos.kt
similarity index 78%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryKosmos.kt
index 6085a1f..12014a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepositoryKosmos.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.statusbar.phone.ongoingcall.data.repository
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.ongoingCallRepository: OngoingCallRepository by
+    Kosmos.Fixture { OngoingCallRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index eb2d6c0..c3c3cce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -32,6 +32,7 @@
 ) : MobileConnectionRepository {
     override val carrierId = MutableStateFlow(UNKNOWN_CARRIER_ID)
     override val inflateSignalStrength: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    override val allowNetworkSliceIndicator: MutableStateFlow<Boolean> = MutableStateFlow(true)
     override val isEmergencyOnly = MutableStateFlow(false)
     override val isRoaming = MutableStateFlow(false)
     override val operatorAlphaShort: MutableStateFlow<String?> = MutableStateFlow(null)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
similarity index 68%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
index 6085a1f..8e656cf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/data/repository/ConnectivityRepositoryKosmos.kt
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.statusbar.pipeline.shared.data.repository
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.fakeConnectivityRepository: FakeConnectivityRepository by
+    Kosmos.Fixture { FakeConnectivityRepository() }
+val Kosmos.connectivityRepository: ConnectivityRepository by
+    Kosmos.Fixture { fakeConnectivityRepository }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
similarity index 97%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 97c8d5f..709be5e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt
index 6085a1f..e44061a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryKosmos.kt
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.statusbar.pipeline.wifi.data.repository
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.fakeWifiRepository: FakeWifiRepository by Kosmos.Fixture { FakeWifiRepository() }
+val Kosmos.wifiRepository: WifiRepository by Kosmos.Fixture { fakeWifiRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.kt
new file mode 100644
index 0000000..7036199
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorKosmos.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.statusbar.pipeline.wifi.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.wifiRepository
+
+val Kosmos.wifiInteractor: WifiInteractor by
+    Kosmos.Fixture {
+        WifiInteractorImpl(
+            connectivityRepository,
+            wifiRepository,
+            applicationCoroutineScope,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyKosmos.kt
index 6085a1f..cf902ef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/DeviceConfigProxyKosmos.kt
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.util
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.kosmos.Kosmos.Fixture
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.fakeDeviceConfigProxy by Fixture { DeviceConfigProxyFake() }
+
+val Kosmos.deviceConfigProxy by Fixture<DeviceConfigProxy> { fakeDeviceConfigProxy }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
index 136b129..3a70cdf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
@@ -22,8 +22,6 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -46,13 +44,6 @@
     }
 
     @Override
-    public CoroutineDispatcher getBackgroundDispatcher() {
-        throw new UnsupportedOperationException(
-                "GlobalSettings.getBackgroundDispatcher is not implemented, but you may find "
-                        + "GlobalSettings.getBackgroundDispatcher helpful instead.");
-    }
-
-    @Override
     public void registerContentObserverSync(Uri uri, boolean notifyDescendants,
             ContentObserver settingsObserver) {
         List<ContentObserver> observers;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
index 6cefa34..cd219ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
@@ -27,8 +27,6 @@
 
 import com.android.systemui.settings.UserTracker;
 
-import kotlinx.coroutines.CoroutineDispatcher;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -68,11 +66,6 @@
     }
 
     @Override
-    public CoroutineDispatcher getBackgroundDispatcher() {
-        return null;
-    }
-
-    @Override
     public void registerContentObserverForUserSync(Uri uri, boolean notifyDescendants,
             ContentObserver settingsObserver, int userHandle) {
         List<ContentObserver> observers;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
index 84ace7c..5fae38f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -18,6 +18,7 @@
 
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastController.Callback;
+import com.android.systemui.statusbar.policy.CastDevice;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt
similarity index 80%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt
index 6085a1f..2f4eef5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryKosmos.kt
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.volume.data.repository
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.volumeDialogRepository by Kosmos.Fixture { VolumeDialogRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt
index 6085a1f..2e5d040 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorKosmos.kt
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.volume.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.volume.data.repository.volumeDialogRepository
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.volumeDialogInteractor by
+    Kosmos.Fixture { VolumeDialogInteractor(volumeDialogRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
similarity index 65%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
index 6085a1f..49170d8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/FpsUnlockTrackerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopupKosmos.kt
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.biometrics
+package com.android.systemui.volume.panel.component.popup.ui.composable
 
+import com.android.systemui.animation.dialogTransitionAnimator
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.statusbar.phone.systemUIDialogFactory
 
-val Kosmos.fpsUnlockTracker by Kosmos.Fixture { mock<FpsUnlockTracker>() }
+val Kosmos.volumePanelPopup: VolumePanelPopup by
+    Kosmos.Fixture { VolumePanelPopup(systemUIDialogFactory, dialogTransitionAnimator) }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt
new file mode 100644
index 0000000..be02487
--- /dev/null
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.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.systemui.unfold.dagger
+
+import javax.inject.Qualifier
+
+/** Qualifier annotation for a progress provider that emits animation events only when
+ * in natural rotation */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class NaturalRotation
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index 4f3aee9..fec6ff1 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -27,6 +27,7 @@
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import java.util.concurrent.CopyOnWriteArrayList
 
 /**
  * Allows to subscribe to rotation changes. Updates are provided for the display associated to
@@ -41,7 +42,7 @@
     @Assisted private val callbackHandler: Handler,
 ) : CallbackController<RotationChangeProvider.RotationListener> {
 
-    private val listeners = mutableListOf<RotationListener>()
+    private val listeners = CopyOnWriteArrayList<RotationListener>()
 
     private val displayListener = RotationDisplayListener()
     private var lastRotation: Int? = null
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
old mode 100755
new mode 100644
diff --git a/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-hdpi/ic_actionbar_accept.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-mdpi/ic_actionbar_accept.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png b/packages/WallpaperCropper/res/drawable-xhdpi/ic_actionbar_accept.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/proto/src/ondeviceintelligence/OWNERS b/proto/src/ondeviceintelligence/OWNERS
new file mode 100644
index 0000000..09774f7
--- /dev/null
+++ b/proto/src/ondeviceintelligence/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/proto/src/ondeviceintelligence/inference_info.proto b/proto/src/ondeviceintelligence/inference_info.proto
new file mode 100644
index 0000000..a6f4f4f
--- /dev/null
+++ b/proto/src/ondeviceintelligence/inference_info.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.ondeviceintelligence;
+
+option java_package = "com.android.server.ondeviceintelligence";
+option java_multiple_files = true;
+
+
+message InferenceInfo {
+  // Uid for the caller app.
+  optional int32 uid = 1;
+  // Inference start time(milliseconds from the epoch time).
+  optional int64 start_time_ms = 2;
+  // Inference end time(milliseconds from the epoch time).
+  optional int64 end_time_ms = 3;
+  // Suspended time in milliseconds.
+  optional int64 suspended_time_ms = 4;
+}
\ No newline at end of file
diff --git a/ravenwood/OWNERS b/ravenwood/OWNERS
index a90328c..badfca0 100644
--- a/ravenwood/OWNERS
+++ b/ravenwood/OWNERS
@@ -2,6 +2,7 @@
 
 jsharkey@google.com
 omakoto@google.com
+dplotnikov@google.com
 
 per-file ravenwood-annotation-allowed-classes.txt = dplotnikov@google.com
 per-file texts/ravenwood-annotation-allowed-classes.txt = dplotnikov@google.com
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
index 61ec7b4..22e11e1 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/Parcel_host.java
@@ -164,6 +164,9 @@
         p.mPos = pos;
     }
     public static void nativeSetDataCapacity(long nativePtr, int size) {
+        if (size < 0) {
+            throw new IllegalArgumentException("size < 0: size=" + size);
+        }
         var p = getInstance(nativePtr);
         if (p.getCapacity() < size) {
             p.forceSetCapacity(size);
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index 2ab43bb..0a0b200 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -6,6 +6,20 @@
 
 When writing tests under Ravenwood, all Android API symbols associated with your declared `sdk_version` are available to link against using, but unsupported APIs will throw an exception.  This design choice enables mocking of unsupported APIs, and supports sharing of test code to build “bivalent” test suites that run against either Ravenwood or a traditional device.
 
+## Manually running tests
+
+To run all Ravenwood tests, use:
+
+```
+./frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh
+```
+
+To run a specific test, use "atest" as normal, selecting the test from a Ravenwood suite such as:
+
+```
+atest CtsOsTestCasesRavenwood:ParcelTest\#testSetDataCapacityNegative
+```
+
 ## Typical test structure
 
 Below are the typical steps needed to add a straightforward “small” unit test:
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 7342b00..edb6390 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1379,30 +1379,6 @@
         }
     }
 
-    @RequiresNoPermission
-    @Override
-    public boolean isMagnificationSystemUIConnected() {
-        if (svcConnTracingEnabled()) {
-            logTraceSvcConn("isMagnificationSystemUIConnected", "");
-        }
-        synchronized (mLock) {
-            if (!hasRightsToCurrentUserLocked()) {
-                return false;
-            }
-            if (!mSecurityPolicy.canControlMagnification(this)) {
-                return false;
-            }
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                MagnificationProcessor magnificationProcessor =
-                        mSystemSupport.getMagnificationProcessor();
-                return magnificationProcessor.isMagnificationSystemUIConnected();
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
     public boolean isMagnificationCallbackEnabled(int displayId) {
         return mInvocationHandler.isMagnificationCallbackEnabled(displayId);
     }
@@ -1947,11 +1923,6 @@
                 InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
     }
 
-    public void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) {
-        mInvocationHandler
-                .notifyMagnificationSystemUIConnectionChangedLocked(connected);
-    }
-
     public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             @NonNull MagnificationConfig config) {
         mInvocationHandler
@@ -2003,21 +1974,6 @@
         return (mGenericMotionEventSources & eventSourceWithoutClass) != 0;
     }
 
-    /**
-     * Called by the invocation handler to notify the service that the
-     * magnification systemui connection has changed.
-     */
-    private void notifyMagnificationSystemUIConnectionChangedInternal(boolean connected) {
-        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
-        if (listener != null) {
-            try {
-                listener.onMagnificationSystemUIConnectionChanged(connected);
-            } catch (RemoteException re) {
-                Slog.e(LOG_TAG,
-                        "Error sending magnification sysui connection changes to " + mService, re);
-            }
-        }
-    }
 
     /**
      * Called by the invocation handler to notify the service that the
@@ -2414,7 +2370,6 @@
         private static final int MSG_BIND_INPUT = 12;
         private static final int MSG_UNBIND_INPUT = 13;
         private static final int MSG_START_INPUT = 14;
-        private static final int MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED = 15;
 
         /** List of magnification callback states, mapping from displayId -> Boolean */
         @GuardedBy("mlock")
@@ -2441,13 +2396,6 @@
                     notifyClearAccessibilityCacheInternal();
                 } break;
 
-                case MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED: {
-                    final SomeArgs args = (SomeArgs) message.obj;
-                    final boolean connected = args.argi1 == 1;
-                    notifyMagnificationSystemUIConnectionChangedInternal(connected);
-                    args.recycle();
-                } break;
-
                 case MSG_ON_MAGNIFICATION_CHANGED: {
                     final SomeArgs args = (SomeArgs) message.obj;
                     final Region region = (Region) args.arg1;
@@ -2505,15 +2453,6 @@
             }
         }
 
-        public void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) {
-            final SomeArgs args = SomeArgs.obtain();
-            args.argi1 = connected ? 1 : 0;
-
-            final Message msg =
-                    obtainMessage(MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED, args);
-            msg.sendToTarget();
-        }
-
         public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
                 @NonNull MagnificationConfig config) {
             synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d09cb3e..4f9db8b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1812,17 +1812,6 @@
     }
 
     /**
-     * Called by the MagnificationController when the magnification systemui connection changes.
-     *
-     * @param connected Whether the connection is ready.
-     */
-    public void notifyMagnificationSystemUIConnectionChanged(boolean connected) {
-        synchronized (mLock) {
-            notifyMagnificationSystemUIConnectionChangedLocked(connected);
-        }
-    }
-
-    /**
      * Called by the MagnificationController when the state of display
      * magnification changes.
      *
@@ -2254,14 +2243,6 @@
         mProxyManager.clearCacheLocked();
     }
 
-    private void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) {
-        final AccessibilityUserState state = getCurrentUserStateLocked();
-        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
-            final AccessibilityServiceConnection service = state.mBoundServices.get(i);
-            service.notifyMagnificationSystemUIConnectionChangedLocked(connected);
-        }
-    }
-
     private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             @NonNull MagnificationConfig config) {
         final AccessibilityUserState state = getCurrentUserStateLocked();
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index 420bac7..4cb3d24 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -489,14 +489,6 @@
     /** @throws UnsupportedOperationException since a proxy does not need magnification */
     @RequiresNoPermission
     @Override
-    public boolean isMagnificationSystemUIConnected() throws UnsupportedOperationException {
-        throw new UnsupportedOperationException("isMagnificationSystemUIConnected is not"
-                + " supported");
-    }
-
-    /** @throws UnsupportedOperationException since a proxy does not need magnification */
-    @RequiresNoPermission
-    @Override
     public boolean isMagnificationCallbackEnabled(int displayId) {
         throw new UnsupportedOperationException("isMagnificationCallbackEnabled is not supported");
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 63a183d..f85d786 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -101,8 +101,10 @@
             SystemActionPerformer systemActionPerformer,
             AccessibilityWindowManager awm, int flags) {
         accessibilityServiceInfo.setComponentName(COMPONENT_NAME);
-        Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s) when called by user %d",
-                accessibilityServiceInfo.getId(), Binder.getCallingUserHandle().getIdentifier());
+        Slogf.i(LOG_TAG, "Registering UiTestAutomationService (id=%s, flags=0x%x) when"
+                        + " called by user %d",
+                accessibilityServiceInfo.getId(), flags,
+                Binder.getCallingUserHandle().getIdentifier());
         if (mUiAutomationService != null) {
             throw new IllegalStateException(
                     "UiAutomationService " + mUiAutomationService.mServiceInterface
@@ -272,8 +274,10 @@
             mMainHandler.post(() -> {
                 try {
                     final IAccessibilityServiceClient serviceInterface;
+                    final UiAutomationService uiAutomationService;
                     synchronized (mLock) {
                         serviceInterface = mServiceInterface;
+                        uiAutomationService = mUiAutomationService;
                         if (serviceInterface == null) {
                             mService = null;
                         } else {
@@ -283,8 +287,8 @@
                     }
                     // If the serviceInterface is null, the UiAutomation has been shut down on
                     // another thread.
-                    if (serviceInterface != null) {
-                        mUiAutomationService.addWindowTokensForAllDisplays();
+                    if (serviceInterface != null && uiAutomationService != null) {
+                        uiAutomationService.addWindowTokensForAllDisplays();
                         if (mTrace.isA11yTracingEnabledForTypes(
                                 AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
                             mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index aa0af7e..b5b998f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -18,6 +18,7 @@
 
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
+import static android.util.MathUtils.abs;
 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
 
@@ -263,27 +264,27 @@
 
         @GuardedBy("mLock")
         boolean isAtEdge() {
-            return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge();
+            return isAtLeftEdge(0f) || isAtRightEdge(0f) || isAtTopEdge(0f) || isAtBottomEdge(0f);
         }
 
         @GuardedBy("mLock")
-        boolean isAtLeftEdge() {
-            return getOffsetX() == getMaxOffsetXLocked();
+        boolean isAtLeftEdge(float slop) {
+            return abs(getOffsetX() - getMaxOffsetXLocked()) <= slop;
         }
 
         @GuardedBy("mLock")
-        boolean isAtRightEdge() {
-            return getOffsetX() == getMinOffsetXLocked();
+        boolean isAtRightEdge(float slop) {
+            return abs(getOffsetX() - getMinOffsetXLocked()) <= slop;
         }
 
         @GuardedBy("mLock")
-        boolean isAtTopEdge() {
-            return getOffsetY() == getMaxOffsetYLocked();
+        boolean isAtTopEdge(float slop) {
+            return abs(getOffsetY() - getMaxOffsetYLocked()) <= slop;
         }
 
         @GuardedBy("mLock")
-        boolean isAtBottomEdge() {
-            return getOffsetY() == getMinOffsetYLocked();
+        boolean isAtBottomEdge(float slop) {
+            return abs(getOffsetY() - getMinOffsetYLocked()) <= slop;
         }
 
         @GuardedBy("mLock")
@@ -1298,7 +1299,7 @@
      * Returns whether the user is at one of the edges (left, right, top, bottom)
      * of the magnification viewport
      *
-     * @param displayId
+     * @param displayId The logical display id.
      * @return if user is at the edge of the view
      */
     public boolean isAtEdge(int displayId) {
@@ -1314,64 +1315,72 @@
     /**
      * Returns whether the user is at the left edge of the viewport
      *
-     * @param displayId
-     * @return if user is at left edge of view
+     * @param displayId The logical display id.
+     * @param slop The buffer distance in pixels from the left edge within that will be considered
+     *             to be at the edge.
+     * @return if user is considered at left edge of view
      */
-    public boolean isAtLeftEdge(int displayId) {
+    public boolean isAtLeftEdge(int displayId, float slop) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return false;
             }
-            return display.isAtLeftEdge();
+            return display.isAtLeftEdge(slop);
         }
     }
 
     /**
      * Returns whether the user is at the right edge of the viewport
      *
-     * @param displayId
-     * @return if user is at right edge of view
+     * @param displayId The logical display id.
+     * @param slop The buffer distance in pixels from the right edge within that will be considered
+     *             to be at the edge.
+     * @return if user is considered at right edge of view
      */
-    public boolean isAtRightEdge(int displayId) {
+    public boolean isAtRightEdge(int displayId, float slop) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return false;
             }
-            return display.isAtRightEdge();
+            return display.isAtRightEdge(slop);
         }
     }
 
     /**
      * Returns whether the user is at the top edge of the viewport
      *
-     * @param displayId
-     * @return if user is at top edge of view
+     * @param displayId The logical display id.
+     * @param slop The buffer distance in pixels from the top edge within that will be considered
+     *             to be at the edge.
+     * @return if user is considered at top edge of view
      */
-    public boolean isAtTopEdge(int displayId) {
+    public boolean isAtTopEdge(int displayId, float slop) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return false;
             }
-            return display.isAtTopEdge();
+            return display.isAtTopEdge(slop);
         }
     }
 
     /**
      * Returns whether the user is at the bottom edge of the viewport
      *
-     * @param displayId
-     * @return if user is at bottom edge of view
+     * @param displayId The logical display id.
+     * @param slop The buffer distance in pixels from the bottom edge within that will be considered
+     *             to be at the edge.
+     * @return if user is considered at bottom edge of view
      */
-    public boolean isAtBottomEdge(int displayId) {
+    public boolean isAtBottomEdge(int displayId, float slop) {
         synchronized (mLock) {
             final DisplayMagnification display = mDisplays.get(displayId);
             if (display == null) {
                 return false;
             }
-            return display.isAtBottomEdge();
+            return display.isAtBottomEdge(slop);
         }
     }
 
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 6d1ab9f..e9c3fbd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -171,7 +171,11 @@
 
     private final FullScreenMagnificationVibrationHelper mFullScreenMagnificationVibrationHelper;
 
-    @VisibleForTesting final OverscrollHandler mOverscrollHandler;
+    @VisibleForTesting
+    @Nullable
+    final OverscrollHandler mOverscrollHandler;
+
+    private final float mOverscrollEdgeSlop;
 
     private final boolean mIsWatch;
 
@@ -308,7 +312,11 @@
         mSinglePanningState = new SinglePanningState(context);
         mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
         mOneFingerPanningSettingsProvider = oneFingerPanningSettingsProvider;
-        mOverscrollHandler = new OverscrollHandler();
+        boolean overscrollHandlerSupported = context.getResources().getBoolean(
+                R.bool.config_enable_a11y_fullscreen_magnification_overscroll_handler);
+        mOverscrollHandler = overscrollHandlerSupported ? new OverscrollHandler() : null;
+        mOverscrollEdgeSlop = context.getResources().getDimensionPixelSize(
+                R.dimen.accessibility_fullscreen_magnification_gesture_edge_slop);
         mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
 
         if (mDetectShortcutTrigger) {
@@ -523,16 +531,14 @@
             if (action == ACTION_POINTER_UP
                     && event.getPointerCount() == 2 // includes the pointer currently being released
                     && mPreviousState == mViewportDraggingState) {
-                // if feature flag is enabled, currently only true on watches
-                if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
+                if (mOverscrollHandler != null) {
                     mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
                     mOverscrollHandler.clearEdgeState();
                 }
                 persistScaleAndTransitionTo(mViewportDraggingState);
             } else if (action == ACTION_UP || action == ACTION_CANCEL) {
                 onPanningFinished(event);
-                // if feature flag is enabled, currently only true on watches
-                if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
+                if (mOverscrollHandler != null) {
                     mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
                     mOverscrollHandler.clearEdgeState();
                 }
@@ -540,7 +546,6 @@
             }
         }
 
-
         void prepareForState() {
             checkShouldDetectPassPersistedScale();
         }
@@ -611,7 +616,7 @@
             onPan(second);
             mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
                     distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
-            if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
+            if (mOverscrollHandler != null) {
                 mOverscrollHandler.onScrollStateChanged(first, second);
             }
             return /* event consumed: */ true;
@@ -1000,7 +1005,7 @@
                                 && event.getPointerCount() == 2) {
                             transitionToViewportDraggingStateAndClear(event);
                         } else if (isActivated() && event.getPointerCount() == 2) {
-                            if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
+                            if (mOverscrollHandler != null
                                     && overscrollState(event, mFirstPointerDownLocation)
                                     == OVERSCROLL_VERTICAL_EDGE) {
                                 transitionToDelegatingStateAndClear();
@@ -1011,9 +1016,13 @@
                         } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
                                 && isActivated()
                                 && event.getPointerCount() == 1) {
-                            if (overscrollState(event, mFirstPointerDownLocation)
+                            if (mOverscrollHandler != null
+                                    && overscrollState(event, mFirstPointerDownLocation)
                                     == OVERSCROLL_VERTICAL_EDGE) {
                                 transitionToDelegatingStateAndClear();
+                            } else if (overscrollState(event, mFirstPointerDownLocation)
+                                    != OVERSCROLL_NONE) {
+                                transitionToDelegatingStateAndClear();
                             } else {
                                 transitToSinglePanningStateAndClear();
                             }
@@ -1255,7 +1264,7 @@
                         if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
                             transitionToViewportDraggingStateAndClear(event);
                         } else if (isActivated() && event.getPointerCount() == 2) {
-                            if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
+                            if (mOverscrollHandler != null
                                     && overscrollState(event, mFirstPointerDownLocation)
                                     == OVERSCROLL_VERTICAL_EDGE) {
                                 transitionToDelegatingStateAndClear();
@@ -1266,9 +1275,13 @@
                         } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
                                 && isActivated()
                                 && event.getPointerCount() == 1) {
-                            if (overscrollState(event, mFirstPointerDownLocation)
+                            if (mOverscrollHandler != null
+                                    && overscrollState(event, mFirstPointerDownLocation)
                                     == OVERSCROLL_VERTICAL_EDGE) {
                                 transitionToDelegatingStateAndClear();
+                            } else if (overscrollState(event, mFirstPointerDownLocation)
+                                    != OVERSCROLL_NONE) {
+                                transitionToDelegatingStateAndClear();
                             } else {
                                 transitToSinglePanningStateAndClear();
                             }
@@ -1645,22 +1658,36 @@
         }
         float dX = event.getX() - firstPointerDownLocation.x;
         float dY = event.getY() - firstPointerDownLocation.y;
-        if (mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0) {
+        if (isAtLeftEdge() && dX > 0) {
             return OVERSCROLL_LEFT_EDGE;
-        } else if (mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0) {
+        } else if (isAtRightEdge() && dX < 0) {
             return OVERSCROLL_RIGHT_EDGE;
-        } else if (mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0
-                || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0) {
+        } else if ((isAtTopEdge() && dY > 0) || (isAtBottomEdge() && dY < 0)) {
             return OVERSCROLL_VERTICAL_EDGE;
         }
         return OVERSCROLL_NONE;
     }
 
+    private boolean isAtLeftEdge() {
+        return mFullScreenMagnificationController.isAtLeftEdge(mDisplayId, mOverscrollEdgeSlop);
+    }
+
+    private boolean isAtRightEdge() {
+        return mFullScreenMagnificationController.isAtRightEdge(mDisplayId, mOverscrollEdgeSlop);
+    }
+
+    private boolean isAtTopEdge() {
+        return mFullScreenMagnificationController.isAtTopEdge(mDisplayId, mOverscrollEdgeSlop);
+    }
+
+    private boolean isAtBottomEdge() {
+        return mFullScreenMagnificationController.isAtBottomEdge(mDisplayId, mOverscrollEdgeSlop);
+    }
+
     private boolean pointerValid(PointF pointerDownLocation) {
         return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(pointerDownLocation.y));
     }
 
-
     private static final class MotionEventInfo {
 
         private static final int MAX_POOL_SIZE = 10;
@@ -1845,7 +1872,6 @@
 
     final class SinglePanningState extends SimpleOnGestureListener implements State {
 
-
         private final GestureDetector mScrollGestureDetector;
         private MotionEventInfo mEvent;
         SinglePanningState(Context context) {
@@ -1860,8 +1886,10 @@
                     onPanningFinished(event);
                     // fall-through!
                 case ACTION_CANCEL:
-                    mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
-                    mOverscrollHandler.clearEdgeState();
+                    if (mOverscrollHandler != null) {
+                        mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
+                        mOverscrollHandler.clearEdgeState();
+                    }
                     transitionTo(mDetectingState);
                     break;
             }
@@ -1889,26 +1917,18 @@
                                 + " isAtEdge: "
                                 + mFullScreenMagnificationController.isAtEdge(mDisplayId));
             }
-            mOverscrollHandler.onScrollStateChanged(first, second);
+            if (mOverscrollHandler != null) {
+                mOverscrollHandler.onScrollStateChanged(first, second);
+            }
             return /* event consumed: */ true;
         }
 
-        private void vibrateIfNeeded() {
-            if ((mFullScreenMagnificationController.isAtLeftEdge(mDisplayId)
-                    || mFullScreenMagnificationController.isAtRightEdge(mDisplayId))) {
-                mFullScreenMagnificationVibrationHelper.vibrateIfSettingEnabled();
-            }
-        }
-
-
-
         @Override
         public String toString() {
             return "SinglePanningState{"
                     + "isEdgeOfView="
                     + mFullScreenMagnificationController.isAtEdge(mDisplayId);
         }
-
     }
 
     /** Overscroll Handler handles the logic when user is at the edge and scrolls past an edge */
@@ -2007,9 +2027,7 @@
             if (mOverscrollState != OVERSCROLL_NONE) {
                 return;
             }
-            if ((mFullScreenMagnificationController.isAtLeftEdge(mDisplayId)
-                            || mFullScreenMagnificationController.isAtRightEdge(mDisplayId))
-                    && !mEdgeCooldown) {
+            if ((isAtLeftEdge() || isAtRightEdge()) && !mEdgeCooldown) {
                 mFullScreenMagnificationVibrationHelper.vibrateIfSettingEnabled();
             }
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index 7f4c808..19e3e69 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -18,6 +18,7 @@
 
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION;
 import static android.accessibilityservice.AccessibilityTrace.FLAGS_MAGNIFICATION_CONNECTION_CALLBACK;
+import static android.os.Build.HW_TIMEOUT_MULTIPLIER;
 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
 
 import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID;
@@ -126,9 +127,8 @@
 
     @ConnectionState
     private int mConnectionState = DISCONNECTED;
-    ConnectionStateChangedCallback mConnectionStateChangedCallback = null;
 
-    private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 100;
+    private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 200 * HW_TIMEOUT_MULTIPLIER;
 
     private final Object mLock;
     private final Context mContext;
@@ -265,9 +265,6 @@
                 }
             }
         }
-        if (mConnectionStateChangedCallback != null) {
-            mConnectionStateChangedCallback.onConnectionStateChanged(connection != null);
-        }
     }
 
     /**
@@ -275,7 +272,7 @@
      */
     public boolean isConnected() {
         synchronized (mLock) {
-            return mConnectionWrapper != null && mConnectionState == CONNECTED;
+            return mConnectionWrapper != null;
         }
     }
 
@@ -683,8 +680,7 @@
      */
     public boolean onFullscreenMagnificationActivationChanged(int displayId, boolean activated) {
         synchronized (mLock) {
-            waitForConnectionIfNeeded();
-            if (mConnectionWrapper == null) {
+            if (!waitConnectionWithTimeoutIfNeeded()) {
                 Slog.w(TAG,
                         "onFullscreenMagnificationActivationChanged mConnectionWrapper is null. "
                                 + "mConnectionState=" + connectionStateToString(mConnectionState));
@@ -1294,8 +1290,7 @@
             float centerY, float magnificationFrameOffsetRatioX,
             float magnificationFrameOffsetRatioY,
             MagnificationAnimationCallback animationCallback) {
-        waitForConnectionIfNeeded();
-        if (mConnectionWrapper == null) {
+        if (!waitConnectionWithTimeoutIfNeeded()) {
             Slog.w(TAG,
                     "enableWindowMagnificationInternal mConnectionWrapper is null. "
                             + "mConnectionState=" + connectionStateToString(mConnectionState));
@@ -1337,7 +1332,7 @@
                 displayId, positionX, positionY, animationCallback);
     }
 
-    private void waitForConnectionIfNeeded() {
+    boolean waitConnectionWithTimeoutIfNeeded() {
         // Wait for the connection with a timeout.
         final long endMillis = SystemClock.uptimeMillis() + WAIT_CONNECTION_TIMEOUT_MILLIS;
         while (mConnectionState == CONNECTING && (SystemClock.uptimeMillis() < endMillis)) {
@@ -1347,9 +1342,6 @@
                 /* ignore */
             }
         }
-    }
-
-    interface ConnectionStateChangedCallback {
-        void onConnectionStateChanged(boolean connected);
+        return isConnected();
     }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 9b78847..1489d16 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -799,14 +799,18 @@
                         this,
                         mScaleProvider,
                         mBackgroundExecutor,
-                        () -> (isMagnificationConnectionManagerInitialized()
-                                && getMagnificationConnectionManager().isConnected())
+                        () -> isMagnificationSystemUIConnectionReady()
                 );
             }
         }
         return mFullScreenMagnificationController;
     }
 
+    private boolean isMagnificationSystemUIConnectionReady() {
+        return isMagnificationConnectionManagerInitialized()
+                && getMagnificationConnectionManager().waitConnectionWithTimeoutIfNeeded();
+    }
+
     /**
      * Is {@link #mFullScreenMagnificationController} is initialized.
      * @return {code true} if {@link #mFullScreenMagnificationController} is initialized.
@@ -828,8 +832,6 @@
                 mMagnificationConnectionManager = new MagnificationConnectionManager(mContext,
                         mLock, this, mAms.getTraceManager(),
                         mScaleProvider);
-                mMagnificationConnectionManager.mConnectionStateChangedCallback =
-                        mAms::notifyMagnificationSystemUIConnectionChanged;
             }
             return mMagnificationConnectionManager;
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index 6036839..ed8f1ab 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -147,10 +147,6 @@
         return false;
     }
 
-    public boolean isMagnificationSystemUIConnected() {
-        return mController.getMagnificationConnectionManager().isConnected();
-    }
-
     private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
             float centerX, float centerY, boolean animate, int id) {
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6fc05b7..eae516e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -2003,7 +2003,7 @@
                 final AutofillManagerServiceImpl service =
                         peekServiceForUserWithLocalBinderIdentityLocked(userId);
                 if (service != null) {
-                    service.setViewAutofilled(sessionId, getCallingUid(), id);
+                    service.setViewAutofilledLocked(sessionId, getCallingUid(), id);
                 } else if (sVerbose) {
                     Slog.v(TAG, "setAutofillFailure(): no service for " + userId);
                 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 588266f..2bf319e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -478,7 +478,7 @@
     }
 
     @GuardedBy("mLock")
-    void setViewAutofilled(int sessionId, int uid, @NonNull AutofillId id) {
+    void setViewAutofilledLocked(int sessionId, int uid, @NonNull AutofillId id) {
         if (!isEnabledLocked()) {
             Slog.wtf(TAG, "Service not enabled");
             return;
@@ -488,7 +488,7 @@
             Slog.v(TAG, "setViewAutofilled(): no session for " + sessionId + "(" + uid + ")");
             return;
         }
-        session.setViewAutofilled(id);
+        session.setViewAutofilledLocked(id);
     }
 
     @GuardedBy("mLock")
@@ -515,7 +515,10 @@
         }
 
         final boolean finished = saveResult.isRemoveSession();
-        if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);
+        if (sVerbose) {
+            Slog.v(TAG, "finishSessionLocked(): session finished? " + finished
+                    + ", showing save UI? " + saveResult.isLogSaveShown());
+        }
 
         if (finished) {
             session.removeFromServiceLocked();
@@ -792,10 +795,9 @@
      * Initializes the last fill selection after an autofill service returned a new
      * {@link FillResponse}.
      */
-    void setLastResponse(int sessionId, @NonNull FillResponse response) {
-        synchronized (mLock) {
+    @GuardedBy("mLock")
+    void setLastResponseLocked(int sessionId, @NonNull FillResponse response) {
             mEventHistory = new FillEventHistory(sessionId, response.getClientState());
-        }
     }
 
     void setLastAugmentedAutofillResponse(int sessionId) {
diff --git a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
index a69e33a..2391268 100644
--- a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
@@ -36,6 +36,7 @@
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_CANCELLED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_FAILURE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_SESSION_DESTROYED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_TRANSACTION_TOO_LARGE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_SUCCESS;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_TIMEOUT;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_UNKNOWN;
@@ -162,6 +163,8 @@
       AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_CANCELLED;
   public static final int RESPONSE_STATUS_FAILURE =
       AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_FAILURE;
+  public static final int RESPONSE_STATUS_TRANSACTION_TOO_LARGE =
+      AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_TRANSACTION_TOO_LARGE;
   public static final int RESPONSE_STATUS_SESSION_DESTROYED =
       AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_SESSION_DESTROYED;
   public static final int RESPONSE_STATUS_SUCCESS =
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index aa76200..d7da2f0 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -295,6 +295,38 @@
         });
     }
 
+    /**
+     * Called for inline suggestions - inflated one at
+     * a time. If InlineSuggestions were filtered,
+     * reset the count be incrementing
+     */
+    public void maybeIncrementCountShown() {
+        mEventInternal.ifPresent(event -> {
+            if (event.shouldResetShownCount) {
+                event.shouldResetShownCount = false;
+                event.mCountShown = 0;
+            }
+
+            if (event.mCountShown == 0) {
+                // The first time suggestions are rendered
+                // set time stamp
+                maybeSetSuggestionPresentedTimestampMs();
+            }
+
+            event.mCountShown += 1;
+        });
+    }
+
+    /**
+     * Call this when UI is hidden. This will set a flag to reset count for inline. We do this
+     * instead of resetting right away in case there are 0 inline presentations after.
+     */
+    public void markShownCountAsResettable() {
+        mEventInternal.ifPresent(event -> {
+            event.shouldResetShownCount = true;
+        });
+    }
+
     public void maybeSetCountShown(@Nullable List<Dataset> datasetList,
             AutofillId currentViewId) {
         mEventInternal.ifPresent(event -> {
@@ -306,6 +338,13 @@
         });
     }
 
+    public void maybeSetCountShown(int datasets) {
+        mEventInternal.ifPresent(
+                event -> {
+                    event.mCountShown = datasets;
+                });
+    }
+
     private static CountContainer getDatasetCountForAutofillId(@Nullable List<Dataset> datasetList,
             AutofillId currentViewId) {
 
@@ -567,31 +606,36 @@
      * <p>If the ViewState contains ViewState.STATE_AUTOFILLED, sets field_autofilled_timestamp_ms
      * else, set field_first_modified_timestamp_ms (if unset) and field_last_modified_timestamp_ms
      */
-    public void onFieldTextUpdated(ViewState state) {
-        mEventInternal.ifPresent(
-                event -> {
+    public void onFieldTextUpdated(ViewState state, int length) {
+        mEventInternal.ifPresent(event -> {
                     int timestamp = getElapsedTime();
                     // Focused id should be set before this is called
-                    if (state.id != null && state.id.getViewId() != event.mFocusedId) {
+                    if (state == null || state.id == null || state.id.getViewId() != event.mFocusedId) {
                         // if these don't match, the currently field different than before
                         Slog.w(
                                 TAG,
-                                "current id: "
-                                        + state.id.getViewId()
-                                        + " is different than focused id: "
-                                        + event.mFocusedId);
+                                "Bad view state for: " + event.mFocusedId);
                         return;
                     }
 
+                    // Text changed because filling into form, just log Autofill timestamp
                     if ((state.getState() & ViewState.STATE_AUTOFILLED) != 0) {
                         event.mAutofilledTimestampMs = timestamp;
-                    } else {
-                        if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
-                            event.mFieldModifiedFirstTimestampMs = timestamp;
-                        }
-                        event.mFieldModifiedLastTimestampMs = timestamp;
+                        return;
                     }
-                });
+
+                    // Set length variables
+                    if (event.mFieldFirstLength == DEFAULT_VALUE_INT) {
+                        event.mFieldFirstLength = length;
+                    }
+                    event.mFieldLastLength = length;
+
+                    // Set timestamp variables
+                    if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
+                        event.mFieldModifiedFirstTimestampMs = timestamp;
+                    }
+                    event.mFieldModifiedLastTimestampMs = timestamp;
+        });
     }
 
     public void setPresentationSelectedTimestamp() {
@@ -641,6 +685,15 @@
     }
 
     /**
+     * Set how many views are filtered from fill because they are not in current session
+     */
+    public void maybeSetFilteredFillableViewsCount(int filteredViewsCount) {
+        mEventInternal.ifPresent(event -> {
+            event.mFilteredFillabaleViewCount = filteredViewsCount;
+        });
+    }
+
+    /**
      * Set views_filled_failure_count using failure count as long as mEventInternal
      * presents.
      */
@@ -652,15 +705,16 @@
 
     /** Sets focused_autofill_id using view id */
     public void maybeSetFocusedId(AutofillId id) {
-        maybeSetFocusedId(id.getViewId());
+        mEventInternal.ifPresent(
+                event -> {
+                    event.mFocusedId = id.getViewId();
+                    if (id.isVirtualInt()) {
+                        event.mFocusedVirtualAutofillId =
+                                id.getVirtualChildIntId() % 100;
+                    }
+                });
     }
 
-    /** Sets focused_autofill_id as long as mEventInternal is present */
-    public void maybeSetFocusedId(int id) {
-        mEventInternal.ifPresent(event -> {
-            event.mFocusedId = id;
-        });
-    }
     /**
      * Set views_filled_failure_count using failure count as long as mEventInternal
      * presents.
@@ -675,7 +729,14 @@
             } else if (autofillIds.contains(autofillId)) {
                 if (sVerbose) {
                     Slog.v(TAG, "Logging autofill for id:" + autofillId);
-                    event.mViewFillSuccessCount++;
+                }
+                event.mViewFillSuccessCount++;
+                autofillIds.remove(autofillId);
+                event.mAlreadyFilledAutofillIds.add(autofillId);
+            } else if (event.mAlreadyFilledAutofillIds.contains(autofillId)) {
+                if (sVerbose) {
+                    Slog.v(TAG, "Successfully filled autofillId:" + autofillId
+                            + " already processed ");
                 }
             } else {
                 Slog.w(TAG, "Successfully filled autofillId:" + autofillId
@@ -728,6 +789,7 @@
                     + " mAppPackageUid=" + mCallingAppUid
                     + " mIsCredentialRequest=" + event.mIsCredentialRequest
                     + " mWebviewRequestedCredential=" + event.mWebviewRequestedCredential
+                    + " mFilteredFillabaleViewCount=" + event.mFilteredFillabaleViewCount
                     + " mViewFillableTotalCount=" + event.mViewFillableTotalCount
                     + " mViewFillFailureCount=" + event.mViewFillFailureCount
                     + " mFocusedId=" + event.mFocusedId
@@ -784,6 +846,7 @@
                 mCallingAppUid,
                 event.mIsCredentialRequest,
                 event.mWebviewRequestedCredential,
+                event.mFilteredFillabaleViewCount,
                 event.mViewFillableTotalCount,
                 event.mViewFillFailureCount,
                 event.mFocusedId,
@@ -805,7 +868,7 @@
         @NotShownReason int mNoPresentationReason = NOT_SHOWN_REASON_UNKNOWN;
         boolean mIsDatasetAvailable;
         int mAvailableCount;
-        int mCountShown;
+        int mCountShown = 0;
         int mCountFilteredUserTyping;
         int mCountNotShownImePresentationNotDrawn;
         int mCountNotShownImeUserNotSeen;
@@ -832,6 +895,7 @@
         int mFieldClassificationRequestId = DEFAULT_VALUE_INT;
         boolean mIsCredentialRequest = false;
         boolean mWebviewRequestedCredential = false;
+        int mFilteredFillabaleViewCount = DEFAULT_VALUE_INT;
         int mViewFillableTotalCount = DEFAULT_VALUE_INT;
         int mViewFillFailureCount = DEFAULT_VALUE_INT;
         int mFocusedId = DEFAULT_VALUE_INT;
@@ -850,6 +914,10 @@
         int mViewFilledButUnexpectedCount = 0;
 
         ArraySet<AutofillId> mAutofillIdsAttemptedAutofill;
+        ArraySet<AutofillId> mAlreadyFilledAutofillIds = new ArraySet<>();
+
+        // Not logged - used for internal logic
+        boolean shouldResetShownCount = false;
         PresentationStatsEventInternal() {}
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 7ceb3bb..07f5dcc 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -82,9 +82,7 @@
         void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
                 @NonNull String servicePackageName, int requestFlags);
 
-        void onFillRequestFailure(int requestId, @Nullable CharSequence message);
-
-        void onFillRequestTimeout(int requestId);
+        void onFillRequestFailure(int requestId, Throwable t);
 
         void onSaveRequestSuccess(@NonNull String servicePackageName,
                 @Nullable IntentSender intentSender);
@@ -345,11 +343,12 @@
                 Slog.e(TAG, "Error calling on fill request", err);
                 if (err instanceof TimeoutException) {
                     dispatchCancellationSignal(cancellationSink.get());
-                    mCallbacks.onFillRequestTimeout(request.getId());
+                    mCallbacks.onFillRequestFailure(request.getId(), err);
                 } else if (err instanceof CancellationException) {
+                    // Cancellation is a part of the user flow - don't mark as failure
                     dispatchCancellationSignal(cancellationSink.get());
                 } else {
-                    mCallbacks.onFillRequestFailure(request.getId(), err.getMessage());
+                    mCallbacks.onFillRequestFailure(request.getId(), err);
                 }
             }
         }));
@@ -413,11 +412,12 @@
                 Slog.e(TAG, "Error calling on fill request", err);
                 if (err instanceof TimeoutException) {
                     dispatchCancellationSignal(cancellationSink.get());
-                    mCallbacks.onFillRequestTimeout(request.getId());
+                    mCallbacks.onFillRequestFailure(request.getId(), err);
                 } else if (err instanceof CancellationException) {
+                    // Cancellation is a part of the user flow - don't mark as failure
                     dispatchCancellationSignal(cancellationSink.get());
                 } else {
-                    mCallbacks.onFillRequestFailure(request.getId(), err.getMessage());
+                    mCallbacks.onFillRequestFailure(request.getId(), err);
                 }
             }
         }));
diff --git a/services/autofill/java/com/android/server/autofill/RequestId.java b/services/autofill/java/com/android/server/autofill/RequestId.java
new file mode 100644
index 0000000..29ad786
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/RequestId.java
@@ -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.server.autofill;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+// Helper class containing various methods to deal with FillRequest Ids.
+// For authentication flows, there needs to be a way to know whether to retrieve the Fill
+// Response from the primary provider or the secondary provider from the requestId. A simple
+// way to achieve this is by assigning odd number request ids to secondary provider and
+// even numbers to primary provider.
+public class RequestId {
+
+  private AtomicInteger sIdCounter;
+
+  // Mainly used for tests
+  RequestId(int start) {
+    sIdCounter = new AtomicInteger(start);
+  }
+
+  public RequestId() {
+    this((int) (Math.floor(Math.random() * 0xFFFF)));
+  }
+
+  public static int getLastRequestIdIndex(List<Integer> requestIds) {
+    int lastId = -1;
+    int indexOfBiggest = -1;
+    // Biggest number is usually the latest request, since IDs only increase
+    // The only exception is when the request ID wraps around back to 0
+      for (int i = requestIds.size() - 1; i >= 0; i--) {
+        if (requestIds.get(i) > lastId) {
+        lastId = requestIds.get(i);
+        indexOfBiggest = i;
+      }
+    }
+
+    // 0xFFFE + 2 == 0x1 (for secondary)
+    // 0xFFFD + 2 == 0x0 (for primary)
+    // Wrap has occurred
+    if (lastId >= 0xFFFD) {
+      // Calculate the biggest size possible
+      // If list only has one kind of request ids - we need to multiple by 2
+      // (since they skip odd ints)
+      // Also subtract one from size because at least one integer exists pre-wrap
+      int calcSize = (requestIds.size()) * 2;
+      //Biggest possible id after wrapping
+      int biggestPossible = (lastId + calcSize) % 0xFFFF;
+      lastId = -1;
+      indexOfBiggest = -1;
+      for (int i = 0; i < requestIds.size(); i++) {
+        int currentId = requestIds.get(i);
+        if (currentId <= biggestPossible && currentId > lastId) {
+          lastId = currentId;
+          indexOfBiggest = i;
+        }
+      }
+    }
+
+    return indexOfBiggest;
+  }
+
+  public int nextId(boolean isSecondary) {
+        // For authentication flows, there needs to be a way to know whether to retrieve the Fill
+        // Response from the primary provider or the secondary provider from the requestId. A simple
+        // way to achieve this is by assigning odd number request ids to secondary provider and
+        // even numbers to primary provider.
+        int requestId;
+
+        do {
+            requestId = sIdCounter.incrementAndGet() % 0xFFFF;
+            sIdCounter.set(requestId);
+        } while (isSecondaryProvider(requestId) != isSecondary);
+        return requestId;
+  }
+
+  public static boolean isSecondaryProvider(int requestId) {
+      return requestId % 2 == 1;
+  }
+}
diff --git a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
index 044a064..a663896 100644
--- a/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
+++ b/services/autofill/java/com/android/server/autofill/SecondaryProviderHandler.java
@@ -75,12 +75,7 @@
     }
 
     @Override
-    public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
-
-    }
-
-    @Override
-    public void onFillRequestTimeout(int requestId) {
+    public void onFillRequestFailure(int requestId, Throwable t) {
 
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index aa67ffe..494e956 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -30,7 +30,6 @@
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_CREDMAN_BOTTOM_SHEET;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
-import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_UNKNOWN;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
@@ -67,6 +66,7 @@
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SESSION_DESTROYED;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SUCCESS;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_TIMEOUT;
+import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_TRANSACTION_TOO_LARGE;
 import static com.android.server.autofill.Helper.containsCharsInOrder;
 import static com.android.server.autofill.Helper.createSanitizers;
 import static com.android.server.autofill.Helper.getNumericValue;
@@ -138,6 +138,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
+import android.os.TransactionTooLargeException;
 import android.service.assist.classification.FieldClassificationRequest;
 import android.service.assist.classification.FieldClassificationResponse;
 import android.service.autofill.AutofillFieldClassificationService.Scores;
@@ -212,6 +213,7 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -264,7 +266,7 @@
 
     static final int AUGMENTED_AUTOFILL_REQUEST_ID = 1;
 
-    private static AtomicInteger sIdCounter = new AtomicInteger(2);
+    private static RequestId mRequestId = new RequestId();
 
     private static AtomicInteger sIdCounterForPcc = new AtomicInteger(2);
 
@@ -1334,7 +1336,7 @@
         }
 
         viewState.setState(newState);
-        int requestId = getRequestId(isSecondary);
+        int requestId = mRequestId.nextId(isSecondary);
 
         // Create a metrics log for the request
         final int ordinal = mRequestLogs.size() + 1;
@@ -1416,25 +1418,6 @@
         requestAssistStructureLocked(requestId, flags);
     }
 
-    private static int getRequestId(boolean isSecondary) {
-        // For authentication flows, there needs to be a way to know whether to retrieve the Fill
-        // Response from the primary provider or the secondary provider from the requestId. A simple
-        // way to achieve this is by assigning odd number request ids to secondary provider and
-        // even numbers to primary provider.
-        int requestId;
-        // TODO(b/158623971): Update this to prevent possible overflow
-        if (isSecondary) {
-            do {
-                requestId = sIdCounter.getAndIncrement();
-            } while (!isSecondaryProviderRequestId(requestId));
-        } else {
-            do {
-                requestId = sIdCounter.getAndIncrement();
-            } while (requestId == INVALID_REQUEST_ID || isSecondaryProviderRequestId(requestId));
-        }
-        return requestId;
-    }
-
     private boolean isRequestSupportFillDialog(int flags) {
         return (flags & FLAG_SUPPORTS_FILL_DIALOG) != 0;
     }
@@ -1442,7 +1425,7 @@
     @GuardedBy("mLock")
     private void requestAssistStructureForPccLocked(int flags) {
         if (!mClassificationState.shouldTriggerRequest()) return;
-        mFillRequestIdSnapshot = sIdCounter.get();
+        mFillRequestIdSnapshot = sIdCounterForPcc.get();
         mClassificationState.updatePendingRequest();
         // Get request id
         int requestId;
@@ -1677,22 +1660,23 @@
 
         final LogMaker requestLog;
 
-        // Start a new FillResponse logger for the success case.
-        mFillResponseEventLogger.startLogForNewResponse();
-        mFillResponseEventLogger.maybeSetRequestId(requestId);
-        mFillResponseEventLogger.maybeSetAppPackageUid(uid);
-        mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS);
-        mFillResponseEventLogger.startResponseProcessingTime();
-        // Time passed since session was created
-        final long fillRequestReceivedRelativeTimestamp =
-            SystemClock.elapsedRealtime() - mLatencyBaseTime;
-        mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs(
-            (int) (fillRequestReceivedRelativeTimestamp));
-        mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
-            (int) (fillRequestReceivedRelativeTimestamp));
-        mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging());
-
         synchronized (mLock) {
+            // Start a new FillResponse logger for the success case.
+            mFillResponseEventLogger.startLogForNewResponse();
+            mFillResponseEventLogger.maybeSetRequestId(requestId);
+            mFillResponseEventLogger.maybeSetAppPackageUid(uid);
+            mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS);
+            mFillResponseEventLogger.startResponseProcessingTime();
+            // Time passed since session was created
+            final long fillRequestReceivedRelativeTimestamp =
+                    SystemClock.elapsedRealtime() - mLatencyBaseTime;
+            mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs(
+                    (int) (fillRequestReceivedRelativeTimestamp));
+            mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
+                    (int) (fillRequestReceivedRelativeTimestamp));
+            mFillResponseEventLogger.maybeSetDetectionPreference(
+                    getDetectionPreferenceForLogging());
+
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
                         + id + " destroyed");
@@ -1744,11 +1728,9 @@
                 Slog.v(TAG, "Service requested to wait for delayed fill response.");
                 registerDelayedFillBroadcastLocked();
             }
-        }
 
-        mService.setLastResponse(id, response);
+            mService.setLastResponseLocked(id, response);
 
-        synchronized (mLock) {
             if (mLogViewEntered) {
                 mLogViewEntered = false;
                 mService.logViewEntered(id, null);
@@ -1821,16 +1803,18 @@
         }
 
         int datasetCount = (datasetList == null) ? 0 : datasetList.size();
-        mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount);
-        // It's possible that this maybe overwritten later on after PCC filtering.
-        mFillResponseEventLogger.maybeSetAvailableCount(datasetCount);
+        synchronized (mLock) {
+            mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount);
+            // It's possible that this maybe overwritten later on after PCC filtering.
+            mFillResponseEventLogger.maybeSetAvailableCount(datasetCount);
 
-        // TODO(b/266379948): Ideally wait for PCC request to finish for a while more
-        // (say 100ms) before proceeding further on.
+            // TODO(b/266379948): Ideally wait for PCC request to finish for a while more
+            // (say 100ms) before proceeding further on.
 
-        processResponseLockedForPcc(response, response.getClientState(), requestFlags);
-        mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
-        mFillResponseEventLogger.logAndEndEvent();
+            processResponseLockedForPcc(response, response.getClientState(), requestFlags);
+            mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
+            mFillResponseEventLogger.logAndEndEvent();
+        }
     }
 
 
@@ -2365,37 +2349,27 @@
     // FillServiceCallbacks
     @Override
     @SuppressWarnings("GuardedBy")
-    public void onFillRequestFailure(int requestId, @Nullable CharSequence message) {
-        onFillRequestFailureOrTimeout(requestId, false, message);
-    }
-
-    // FillServiceCallbacks
-    @Override
-    @SuppressWarnings("GuardedBy")
-    public void onFillRequestTimeout(int requestId) {
-        onFillRequestFailureOrTimeout(requestId, true, null);
-    }
-
-    @SuppressWarnings("GuardedBy")
-    private void onFillRequestFailureOrTimeout(int requestId, boolean timedOut,
-            @Nullable CharSequence message) {
+    public void onFillRequestFailure(int requestId, Throwable t) {
+        CharSequence message = t.getMessage();
+        boolean timedOut = (t instanceof TimeoutException);
         boolean showMessage = !TextUtils.isEmpty(message);
 
-        // Start a new FillResponse logger for the failure or timeout case.
-        mFillResponseEventLogger.startLogForNewResponse();
-        mFillResponseEventLogger.maybeSetRequestId(requestId);
-        mFillResponseEventLogger.maybeSetAppPackageUid(uid);
-        mFillResponseEventLogger.maybeSetAvailableCount(
-                AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
-        mFillResponseEventLogger.maybeSetTotalDatasetsProvided(
-                AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
-        mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging());
-        final long fillRequestReceivedRelativeTimestamp =
-            SystemClock.elapsedRealtime() - mLatencyBaseTime;
-        mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
-            (int)(fillRequestReceivedRelativeTimestamp));
-
         synchronized (mLock) {
+            // Start a new FillResponse logger for the failure or timeout case.
+            mFillResponseEventLogger.startLogForNewResponse();
+            mFillResponseEventLogger.maybeSetRequestId(requestId);
+            mFillResponseEventLogger.maybeSetAppPackageUid(uid);
+            mFillResponseEventLogger.maybeSetAvailableCount(
+                    AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+            mFillResponseEventLogger.maybeSetTotalDatasetsProvided(
+                    AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+            mFillResponseEventLogger.maybeSetDetectionPreference(
+                    getDetectionPreferenceForLogging());
+            final long fillRequestReceivedRelativeTimestamp =
+                    SystemClock.elapsedRealtime() - mLatencyBaseTime;
+            mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
+                    (int) (fillRequestReceivedRelativeTimestamp));
+
             unregisterDelayedFillBroadcastLocked();
             if (mDestroyed) {
                 Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId
@@ -2430,10 +2404,15 @@
                 }
             }
 
-            if (timedOut) {
+            if (t instanceof TimeoutException) {
                 mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
                         NOT_SHOWN_REASON_REQUEST_TIMEOUT);
                 mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_TIMEOUT);
+            } else if (t instanceof TransactionTooLargeException) {
+                mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                        NOT_SHOWN_REASON_REQUEST_FAILED);
+                mFillResponseEventLogger.maybeSetResponseStatus(
+                        RESPONSE_STATUS_TRANSACTION_TOO_LARGE);
             } else {
                 mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
                         NOT_SHOWN_REASON_REQUEST_FAILED);
@@ -2635,8 +2614,8 @@
                         + id + " destroyed");
                 return;
             }
+            mSaveEventLogger.maybeSetLatencySaveRequestMillis();
         }
-        mSaveEventLogger.maybeSetLatencySaveRequestMillis();
         mHandler.sendMessage(obtainMessage(
                 AutofillManagerServiceImpl::handleSessionSave,
                 mService, this));
@@ -2659,19 +2638,30 @@
 
     // AutofillUiCallback
     @Override
-    public void onShown(int uiType) {
+    public void onShown(int uiType, int numDatasetsShown) {
         synchronized (mLock) {
+            mPresentationStatsEventLogger.maybeSetDisplayPresentationType(uiType);
+
             if (uiType == UI_TYPE_INLINE) {
-                if (mLoggedInlineDatasetShown) {
+                // Inline Suggestions are inflated one at a time
+                // This number will be reset when filtered
+                // This will also call maybeSetSuggestionPresentedTimestampMs
+                mPresentationStatsEventLogger.maybeIncrementCountShown();
+
+                if (!mLoggedInlineDatasetShown) {
                     // Chip inflation already logged, do not log again.
                     // This is needed because every chip inflation will call this.
-                    return;
+                    mService.logDatasetShown(this.id, mClientState, uiType);
+                    Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
                 }
                 mLoggedInlineDatasetShown = true;
+            } else {
+                mPresentationStatsEventLogger.maybeSetCountShown(numDatasetsShown);
+                // Explicitly sets maybeSetSuggestionPresentedTimestampMs
+                mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
+                mService.logDatasetShown(this.id, mClientState, uiType);
+                Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
             }
-            mService.logDatasetShown(this.id, mClientState, uiType);
-            mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
-            Slog.d(TAG, "onShown(): " + uiType);
         }
     }
 
@@ -2737,6 +2727,7 @@
             }
 
             mInlineSessionController.hideInlineSuggestionsUiLocked(id);
+            mPresentationStatsEventLogger.markShownCountAsResettable();
         }
     }
 
@@ -2866,18 +2857,18 @@
             // the auth UI.
             Slog.w(TAG, "setAuthenticationResultLocked(" + authenticationId + "): no responses");
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
-                AUTHENTICATION_RESULT_FAILURE);
+                    AUTHENTICATION_RESULT_FAILURE);
             mPresentationStatsEventLogger.logAndEndEvent();
             removeFromService();
             return;
         }
-        final FillResponse authenticatedResponse = isSecondaryProviderRequestId(requestId)
+        final FillResponse authenticatedResponse = mRequestId.isSecondaryProvider(requestId)
                 ? mSecondaryResponses.get(requestId)
                 : mResponses.get(requestId);
         if (authenticatedResponse == null || data == null) {
             Slog.w(TAG, "no authenticated response");
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
-                AUTHENTICATION_RESULT_FAILURE);
+                    AUTHENTICATION_RESULT_FAILURE);
             mPresentationStatsEventLogger.logAndEndEvent();
             removeFromService();
             return;
@@ -2892,7 +2883,7 @@
             if (dataset == null) {
                 Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
                 mPresentationStatsEventLogger.maybeSetAuthenticationResult(
-                    AUTHENTICATION_RESULT_FAILURE);
+                        AUTHENTICATION_RESULT_FAILURE);
                 mPresentationStatsEventLogger.logAndEndEvent();
                 removeFromService();
                 return;
@@ -2933,7 +2924,7 @@
             }
             logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
-                AUTHENTICATION_RESULT_SUCCESS);
+                    AUTHENTICATION_RESULT_SUCCESS);
             replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
         } else if (result instanceof GetCredentialResponse) {
             if (sDebug) {
@@ -2967,9 +2958,10 @@
                 logAuthenticationStatusLocked(requestId,
                         MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
                 mPresentationStatsEventLogger.maybeSetAuthenticationResult(
-                    AUTHENTICATION_RESULT_SUCCESS);
+                        AUTHENTICATION_RESULT_SUCCESS);
                 if (newClientState != null) {
-                    if (sDebug) Slog.d(TAG,  "Updating client state from auth dataset");
+                    if (sDebug)
+                        Slog.d(TAG, "Updating client state from auth dataset");
                     mClientState = newClientState;
                 }
                 Dataset datasetFromResult = getEffectiveDatasetForAuthentication((Dataset) result);
@@ -2984,7 +2976,7 @@
                 logAuthenticationStatusLocked(requestId,
                         MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION);
                 mPresentationStatsEventLogger.maybeSetAuthenticationResult(
-                    AUTHENTICATION_RESULT_FAILURE);
+                        AUTHENTICATION_RESULT_FAILURE);
             }
         } else {
             if (result != null) {
@@ -2993,15 +2985,11 @@
             logAuthenticationStatusLocked(requestId,
                     MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION);
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
-                AUTHENTICATION_RESULT_FAILURE);
+                    AUTHENTICATION_RESULT_FAILURE);
             processNullResponseLocked(requestId, 0);
         }
     }
 
-    private static boolean isSecondaryProviderRequestId(int requestId) {
-        return requestId % 2 == 1;
-    }
-
     private Dataset getDatasetFromCredentialResponse(GetCredentialResponse result) {
         if (result == null) {
             return null;
@@ -3215,6 +3203,58 @@
         return saveInfo == null ? 0 : saveInfo.getFlags();
     }
 
+    static class SaveInfoStats {
+        public int saveInfoCount;
+        public int saveDataTypeCount;
+    }
+
+    /**
+     * Get statistic information of save info in current session. Specifically
+     *   1. how many save info the current session has.
+     *   2. How many distinct save data types current session has.
+     *
+     * @return SaveInfoStats returns the above two number in a SaveInfoStats object
+     */
+    @GuardedBy("mLock")
+    private SaveInfoStats getSaveInfoStatsLocked() {
+        SaveInfoStats retSaveInfoStats = new SaveInfoStats();
+        retSaveInfoStats.saveInfoCount = -1;
+        retSaveInfoStats.saveDataTypeCount = -1;
+
+        if (mContexts == null) {
+            if (sVerbose) {
+                Slog.v(TAG, "getSaveInfoStatsLocked(): mContexts is null");
+            }
+        } else if (mResponses == null) {
+            // Happens when the activity / session was finished before the service replied, or
+            // when the service cannot autofill it (and returned a null response).
+            if (sVerbose) {
+                Slog.v(TAG, "getSaveInfoStatsLocked(): mResponses is null");
+            }
+            return retSaveInfoStats;
+        } else {
+            int numSaveInfos = 0;
+            int numSaveDataTypes = 0;
+            ArraySet<Integer> saveDataTypeSeen = new ArraySet<>();
+            final int numResponses = mResponses.size();
+            for (int responseNum = 0; responseNum < numResponses; responseNum++) {
+                final FillResponse response = mResponses.valueAt(responseNum);
+                if (response != null && response.getSaveInfo() != null) {
+                    numSaveInfos += 1;
+                    int saveDataType = response.getSaveInfo().getType();
+                    if (!saveDataTypeSeen.contains(saveDataType)) {
+                        saveDataTypeSeen.add(saveDataType);
+                        numSaveDataTypes += 1;
+                    }
+                }
+            }
+            retSaveInfoStats.saveInfoCount = numSaveInfos;
+            retSaveInfoStats.saveDataTypeCount = numSaveDataTypes;
+        }
+
+        return retSaveInfoStats;
+    }
+
     /**
      * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}
      * when necessary.
@@ -3227,7 +3267,9 @@
         mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this,
                 Event.NO_SAVE_UI_REASON_NONE,
                 COMMIT_REASON_UNKNOWN));
-        logAllEvents(COMMIT_REASON_UNKNOWN);
+        synchronized (mLock) {
+            logAllEventsLocked(COMMIT_REASON_UNKNOWN);
+        }
     }
 
     /**
@@ -3251,6 +3293,11 @@
 
         mSessionCommittedEventLogger.maybeSetCommitReason(commitReason);
         mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount);
+        SaveInfoStats saveInfoStats = getSaveInfoStatsLocked();
+        mSessionCommittedEventLogger.maybeSetSaveInfoCount(saveInfoStats.saveInfoCount);
+        mSessionCommittedEventLogger.maybeSetSaveDataTypeCount(saveInfoStats.saveDataTypeCount);
+        mSessionCommittedEventLogger.maybeSetLastFillResponseHasSaveInfo(
+                getSaveInfoLocked() != null);
         mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE);
     }
 
@@ -4571,7 +4618,7 @@
 
                     FillResponse response = viewState.getSecondaryResponse();
                     if (response != null) {
-                        logPresentationStatsOnViewEntered(response, isCredmanRequested);
+                        logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested);
                     }
 
                     // If the ViewState is ready to be displayed, onReady() will be called.
@@ -4662,7 +4709,7 @@
 
                 FillResponse response = viewState.getResponse();
                 if (response != null) {
-                    logPresentationStatsOnViewEntered(response, isCredmanRequested);
+                    logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested);
                 }
 
                 if (isSameViewEntered) {
@@ -4703,7 +4750,7 @@
     }
 
     @GuardedBy("mLock")
-    private void logPresentationStatsOnViewEntered(FillResponse response,
+    private void logPresentationStatsOnViewEnteredLocked(FillResponse response,
             boolean isCredmanRequested) {
         mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
         mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
@@ -4807,7 +4854,9 @@
                 currentView.maybeCallOnFillReady(flags);
             }
         }
-        mPresentationStatsEventLogger.onFieldTextUpdated(viewState);
+        if (textValue != null) {
+            mPresentationStatsEventLogger.onFieldTextUpdated(viewState, textValue.length());
+        }
 
         if (viewState.id.equals(this.mCurrentViewId)
                 && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
@@ -4904,8 +4953,6 @@
                 synchronized (mLock) {
                     final ViewState currentView = mViewStates.get(mCurrentViewId);
                     currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
-                    mPresentationStatsEventLogger.maybeSetCountShown(
-                            response.getDatasets(), mCurrentViewId);
                     mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_DIALOG);
                 }
                 // Just show fill dialog once, so disabled after shown.
@@ -4926,10 +4973,6 @@
                     // back a response via callback.
                     final ViewState currentView = mViewStates.get(mCurrentViewId);
                     currentView.setState(ViewState.STATE_INLINE_SHOWN);
-                    // TODO(b/234475358): Log more accurate value of number of inline suggestions
-                    // shown, inflated, and filtered.
-                    mPresentationStatsEventLogger.maybeSetCountShown(
-                            response.getDatasets(), mCurrentViewId);
                     mPresentationStatsEventLogger.maybeSetInlinePresentationAndSuggestionHostUid(
                             mContext, userId);
                     return;
@@ -4943,12 +4986,6 @@
                 mService.getMaster().getMaxInputLengthForAutofill());
 
         synchronized (mLock) {
-            mPresentationStatsEventLogger.maybeSetCountShown(
-                    response.getDatasets(), mCurrentViewId);
-            mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_MENU);
-        }
-
-        synchronized (mLock) {
             if (mUiShownTime == 0) {
                 // Log first time UI is shown.
                 mUiShownTime = SystemClock.elapsedRealtime();
@@ -5061,7 +5098,7 @@
 
         getUiForShowing().showFillDialog(filledId, response, filterText,
                 mService.getServicePackageName(), mComponentName, serviceIcon, this,
-                id, mCompatMode, mPresentationStatsEventLogger);
+                id, mCompatMode, mPresentationStatsEventLogger, mLock);
         return true;
     }
 
@@ -5188,7 +5225,7 @@
 
                     @Override
                     public void onInflate() {
-                        Session.this.onShown(UI_TYPE_INLINE);
+                        Session.this.onShown(UI_TYPE_INLINE, 1);
                     }
                 }, mService.getMaster().getMaxInputLengthForAutofill());
         return mInlineSessionController.setInlineFillUiLocked(inlineFillUi);
@@ -5380,9 +5417,10 @@
 
         try {
             if (sVerbose) {
-                Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
-                        + " triggerId: " + saveTriggerId + " saveOnFinish:" + saveOnFinish
-                        + " flags: " + flags + " hasSaveInfo: " + (saveInfo != null));
+                Slog.v(TAG, "updateTrackedIdsLocked(): trackedViews: " + trackedViews
+                        + " fillableIds: " + fillableIds + " triggerId: " + saveTriggerId
+                        + " saveOnFinish:" + saveOnFinish + " flags: " + flags
+                        + " hasSaveInfo: " + (saveInfo != null));
             }
             mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible,
                     saveOnFinish, toArray(fillableIds), saveTriggerId);
@@ -5420,7 +5458,7 @@
      * Sets the state of views that failed to autofill.
      */
     @GuardedBy("mLock")
-    void setViewAutofilled(@NonNull AutofillId id) {
+    void setViewAutofilledLocked(@NonNull AutofillId id) {
         if (sVerbose) {
             Slog.v(TAG, "View autofilled: " + id);
         }
@@ -6603,6 +6641,8 @@
                 boolean waitingDatasetAuth = false;
                 boolean hideHighlight = (entryCount == 1
                         && dataset.getFieldIds().get(0).equals(mCurrentViewId));
+                // Count how many views are filtered because they are not in current session
+                int numOfViewsFiltered = 0;
                 for (int i = 0; i < entryCount; i++) {
                     if (dataset.getFieldValues().get(i) == null) {
                         continue;
@@ -6615,6 +6655,7 @@
                             Slog.v(TAG, "Skipping filling view: " +
                                     viewId + " as it isn't part of the current session: " + id);
                         }
+                        numOfViewsFiltered += 1;
                         continue;
                     }
                     ids.add(viewId);
@@ -6628,6 +6669,8 @@
                         viewState.resetState(ViewState.STATE_WAITING_DATASET_AUTH);
                     }
                 }
+                mPresentationStatsEventLogger.maybeSetFilteredFillableViewsCount(
+                        numOfViewsFiltered);
                 if (!ids.isEmpty()) {
                     if (waitingDatasetAuth) {
                         mUi.hideFillUi(this);
@@ -6663,7 +6706,7 @@
     }
 
     @GuardedBy("mLock")
-    private void logAllEvents(@AutofillCommitReason int val) {
+    private void logAllEventsLocked(@AutofillCommitReason int val) {
         if (sVerbose) {
             Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val);
         }
@@ -6695,7 +6738,7 @@
         if (sVerbose) {
             Slog.v(TAG, "destroyLocked for session: " + id);
         }
-        logAllEvents(COMMIT_REASON_SESSION_DESTROYED);
+        logAllEventsLocked(COMMIT_REASON_SESSION_DESTROYED);
 
         if (mDestroyed) {
             return null;
@@ -6861,22 +6904,15 @@
 
     @GuardedBy("mLock")
     private int getLastResponseIndexLocked() {
-        // The response ids are monotonically increasing so
-        // we just find the largest id which is the last. We
-        // do not rely on the internal ordering in sparse
-        // array to avoid - wow this stopped working!?
-        int lastResponseIdx = -1;
-        int lastResponseId = -1;
         if (mResponses != null) {
+            List<Integer> requestIdList = new ArrayList<>();
             final int responseCount = mResponses.size();
             for (int i = 0; i < responseCount; i++) {
-                if (mResponses.keyAt(i) > lastResponseId) {
-                    lastResponseIdx = i;
-                    lastResponseId = mResponses.keyAt(i);
-                }
+                requestIdList.add(mResponses.keyAt(i));
             }
+            return mRequestId.getLastRequestIdIndex(requestIdList);
         }
-        return lastResponseIdx;
+        return -1;
     }
 
     private LogMaker newLogMaker(int category) {
diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
index 1be8548..8f3c880 100644
--- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java
@@ -94,6 +94,33 @@
   }
 
   /**
+   * Set how many save infos there are in current session as long as mEventInternal presents.
+   */
+  public void maybeSetSaveInfoCount(int saveInfoCount) {
+        mEventInternal.ifPresent(event -> {
+            event.mSaveInfoCount = saveInfoCount;
+        });
+  }
+
+  /**
+   * Set how many save data types there are in current session as long as mEventInternal presents.
+   */
+  public void maybeSetSaveDataTypeCount(int saveDataTypeCount) {
+        mEventInternal.ifPresent(event -> {
+            event.mSaveDataTypeCount = saveDataTypeCount;
+        });
+  }
+
+  /**
+   * Set whether last fill response in session has save info as long as mEventInternal presents.
+   */
+  public void maybeSetLastFillResponseHasSaveInfo(boolean lastFillResponseHasSaveInfo) {
+        mEventInternal.ifPresent(event -> {
+            event.mLastFillResponseHasSaveInfo = lastFillResponseHasSaveInfo;
+        });
+  }
+
+  /**
    * Log an AUTOFILL_SESSION_COMMITTED event.
    */
   public void logAndEndEvent() {
@@ -109,7 +136,10 @@
           + " mRequestCount=" + event.mRequestCount
           + " mCommitReason=" + event.mCommitReason
           + " mSessionDurationMillis=" + event.mSessionDurationMillis
-          + " mServiceUid=" + event.mServiceUid);
+          + " mServiceUid=" + event.mServiceUid
+          + " mSaveInfoCount=" + event.mSaveInfoCount
+          + " mSaveDataTypeCount=" + event.mSaveDataTypeCount
+          + " mLastFillResponseHasSaveInfo=" + event.mLastFillResponseHasSaveInfo);
     }
     FrameworkStatsLog.write(
         AUTOFILL_SESSION_COMMITTED,
@@ -118,7 +148,10 @@
         event.mRequestCount,
         event.mCommitReason,
         event.mSessionDurationMillis,
-        event.mServiceUid);
+        event.mServiceUid,
+        event.mSaveInfoCount,
+        event.mSaveDataTypeCount,
+        event.mLastFillResponseHasSaveInfo);
     mEventInternal = Optional.empty();
   }
 
@@ -127,6 +160,9 @@
     int mRequestCount = 0;
     int mCommitReason = COMMIT_REASON_UNKNOWN;
     long mSessionDurationMillis = 0;
+    int mSaveInfoCount = -1;
+    int mSaveDataTypeCount = -1;
+    boolean mLastFillResponseHasSaveInfo = false;
     int mServiceUid = -1;
 
     SessionCommittedEventInternal() {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 3b9c54f..2e9a4dc 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -102,7 +102,7 @@
         void cancelSession();
         void requestShowSoftInput(AutofillId id);
         void requestFallbackFromFillDialog();
-        void onShown(int uiType);
+        void onShown(int uiType, int datasetSize);
     }
 
     public AutoFillUI(@NonNull Context context) {
@@ -246,9 +246,9 @@
                 }
 
                 @Override
-                public void onShown() {
+                public void onShown(int datasetSize) {
                     if (mCallback != null) {
-                        mCallback.onShown(UI_TYPE_MENU);
+                        mCallback.onShown(UI_TYPE_MENU, datasetSize);
                     }
                 }
 
@@ -425,7 +425,8 @@
             @Nullable String filterText, @Nullable String servicePackageName,
             @NonNull ComponentName componentName, @Nullable Drawable serviceIcon,
             @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode,
-            @Nullable PresentationStatsEventLogger mPresentationStatsEventLogger) {
+            @Nullable PresentationStatsEventLogger presentationStatsEventLogger,
+            @NonNull Object sessionLock) {
         if (sVerbose) {
             Slog.v(TAG, "showFillDialog for "
                     + componentName.toShortString() + ": " + response);
@@ -461,15 +462,17 @@
 
                         @Override
                         public void onShown() {
-                            callback.onShown(UI_TYPE_DIALOG);
+                            mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size());
                         }
 
                         @Override
                         public void onDatasetPicked(Dataset dataset) {
                             log(MetricsEvent.TYPE_ACTION);
-                            if (mPresentationStatsEventLogger != null) {
-                                mPresentationStatsEventLogger.maybeSetPositiveCtaButtonClicked(
-                                    true);
+                            synchronized (sessionLock) {
+                                if (presentationStatsEventLogger != null) {
+                                    presentationStatsEventLogger.maybeSetPositiveCtaButtonClicked(
+                                            true);
+                                }
                             }
                             hideFillDialogUiThread(callback);
                             if (mCallback != null) {
@@ -482,8 +485,10 @@
                         @Override
                         public void onDismissed() {
                             log(MetricsEvent.TYPE_DISMISS);
-                            if (mPresentationStatsEventLogger != null) {
-                                mPresentationStatsEventLogger.maybeSetDialogDismissed(true);
+                            synchronized (sessionLock) {
+                                if (presentationStatsEventLogger != null) {
+                                    presentationStatsEventLogger.maybeSetDialogDismissed(true);
+                                }
                             }
                             hideFillDialogUiThread(callback);
                             callback.requestShowSoftInput(focusedId);
@@ -493,9 +498,11 @@
                         @Override
                         public void onCanceled() {
                             log(MetricsEvent.TYPE_CLOSE);
-                            if (mPresentationStatsEventLogger != null) {
-                                mPresentationStatsEventLogger.maybeSetNegativeCtaButtonClicked(
-                                    true);
+                            synchronized (sessionLock) {
+                                if (presentationStatsEventLogger != null) {
+                                    presentationStatsEventLogger.maybeSetNegativeCtaButtonClicked(
+                                            true);
+                                }
                             }
                             hideFillDialogUiThread(callback);
                             callback.requestShowSoftInput(focusedId);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 78edb8e..1bda70d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -16,6 +16,7 @@
 package com.android.server.autofill.ui;
 
 import static android.service.autofill.FillResponse.FLAG_CREDENTIAL_MANAGER_RESPONSE;
+
 import static com.android.server.autofill.Helper.paramsToString;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sFullScreenMode;
@@ -90,7 +91,7 @@
         void onDatasetPicked(@NonNull Dataset dataset);
         void onCanceled();
         void onDestroy();
-        void onShown();
+        void onShown(int datasetSize);
         void requestShowFillUi(int width, int height,
                 IAutofillWindowPresenter windowPresenter);
         void requestHideFillUi();
@@ -689,12 +690,20 @@
                 Slog.v(TAG, "AutofillWindowPresenter.show(): fit=" + fitsSystemWindows
                         + ", params=" + paramsToString(p));
             }
-            UiThread.getHandler().post(() -> mWindow.show(p));
+            UiThread.getHandler().post(() -> {
+                if (mWindow != null) {
+                    mWindow.show(p);
+                }
+            });
         }
 
         @Override
         public void hide(Rect transitionEpicenter) {
-            UiThread.getHandler().post(mWindow::hide);
+            UiThread.getHandler().post(() -> {
+                if (mWindow != null) {
+                    mWindow.hide();
+                }
+            });
         }
     }
 
@@ -734,7 +743,8 @@
                     mWm.addView(mContentView, params);
                     mOverlayControl.hideOverlays();
                     mShowing = true;
-                    mCallback.onShown();
+                    int numShownDatasets = (mAdapter == null) ? 0 : mAdapter.getCount();
+                    mCallback.onShown(numShownDatasets);
                 } else {
                     mWm.updateViewLayout(mContentView, params);
                 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0d0c21d..0ab6bbc 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -18,6 +18,7 @@
 package com.android.server.companion;
 
 import static android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
 import static android.Manifest.permission.DELIVER_COMPANION_MESSAGES;
 import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
 import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED;
@@ -52,6 +53,9 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.ecm.EnhancedConfirmationManager;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
 import android.companion.AssociationInfo;
 import android.companion.AssociationRequest;
 import android.companion.IAssociationRequestCallback;
@@ -528,7 +532,8 @@
                 String packageName, int userId) {
             startObservingDevicePresence_enforcePermission();
 
-            mDevicePresenceProcessor.startObservingDevicePresence(request, packageName, userId);
+            mDevicePresenceProcessor.startObservingDevicePresence(
+                    request, packageName, userId, /* enforcePermissions */ true);
         }
 
         @Override
@@ -537,7 +542,33 @@
                 String packageName, int userId) {
             stopObservingDevicePresence_enforcePermission();
 
-            mDevicePresenceProcessor.stopObservingDevicePresence(request, packageName, userId);
+            mDevicePresenceProcessor.stopObservingDevicePresence(
+                    request, packageName, userId, /* enforcePermissions */ true);
+        }
+
+        @Override
+        @EnforcePermission(BLUETOOTH_CONNECT)
+        public boolean removeBond(int associationId, String packageName, int userId) {
+            removeBond_enforcePermission();
+
+            Slog.i(TAG, "removeBond() "
+                    + "associationId=" + associationId + ", "
+                    + "package=u" + userId + "/" + packageName);
+            enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
+                    "remove bonds");
+
+            AssociationInfo association = mAssociationStore
+                    .getAssociationWithCallerChecks(associationId);
+            MacAddress address = association.getDeviceMacAddress();
+            if (address == null) {
+                throw new IllegalArgumentException(
+                        "Association id=[" + associationId + "] doesn't have a device address.");
+            }
+
+            BluetoothAdapter btAdapter = getContext().getSystemService(BluetoothManager.class)
+                    .getAdapter();
+            BluetoothDevice btDevice = btAdapter.getRemoteDevice(address.toString().toUpperCase());
+            return btDevice.removeBond();
         }
 
         @Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index c892b84..3d53deb 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -21,6 +21,7 @@
 import android.companion.AssociationInfo;
 import android.companion.ContextSyncMessage;
 import android.companion.Flags;
+import android.companion.ObservingDevicePresenceRequest;
 import android.companion.Telecom;
 import android.companion.datatransfer.PermissionSyncRequest;
 import android.net.MacAddress;
@@ -193,6 +194,43 @@
                     break;
                 }
 
+                case "start-observing-device-presence-uuid": {
+                    if (Flags.devicePresence()) {
+                        int userId = getNextIntArgRequired();
+                        String packageName = getNextArgRequired();
+                        String uuid = getNextArgRequired();
+                        if ("null".equals(uuid)) {
+                            out.println("UUID can not be null.");
+                            break;
+                        }
+                        ParcelUuid parcelUuid = ParcelUuid.fromString(uuid);
+                        ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest
+                                .Builder().setUuid(parcelUuid).build();
+                        mDevicePresenceProcessor.startObservingDevicePresence(
+                                request, packageName, userId, /* enforcePermissions */ false);
+
+                    }
+                    break;
+                }
+
+                case "stop-observing-device-presence-uuid": {
+                    if (Flags.devicePresence()) {
+                        int userId = getNextIntArgRequired();
+                        String packageName = getNextArgRequired();
+                        String uuid = getNextArgRequired();
+                        if ("null".equals(uuid)) {
+                            out.println("UUID can not be null.");
+                            break;
+                        }
+                        ParcelUuid parcelUuid = ParcelUuid.fromString(uuid);
+                        ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest
+                                .Builder().setUuid(parcelUuid).build();
+                        mDevicePresenceProcessor.stopObservingDevicePresence(
+                                request, packageName, userId, /* enforcePermissions */ false);
+                    }
+                    break;
+                }
+
                 case "get-backup-payload": {
                     final int userId = getNextIntArgRequired();
                     byte[] payload = mBackupRestoreProcessor.getBackupPayload(userId);
@@ -515,6 +553,14 @@
             pw.println("  callback after simulate-device-event-device-locked");
             pw.println("  command has been called.");
             pw.println("  USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+            pw.println("  start-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID");
+            pw.println("  Start observing device presence base on the UUID.");
+            pw.println("  USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+            pw.println("  stop-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID");
+            pw.println("  Stop observing device presence base on the UUID.");
+            pw.println("  USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
         }
 
         pw.println("  remove-inactive-associations");
diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
index af49df6..a374d27 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java
@@ -181,14 +181,16 @@
      * Process device presence start request.
      */
     public void startObservingDevicePresence(ObservingDevicePresenceRequest request,
-            String packageName, int userId) {
+            String packageName, int userId, boolean enforcePermissions) {
         Slog.i(TAG,
                 "Start observing request=[" + request + "] for userId=[" + userId + "], package=["
                         + packageName + "]...");
         final ParcelUuid requestUuid = request.getUuid();
 
         if (requestUuid != null) {
-            enforceCallerCanObserveDevicePresenceByUuid(mContext);
+            if (enforcePermissions) {
+                enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId);
+            }
 
             // If it's already being observed, then no-op.
             if (mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) {
@@ -236,7 +238,7 @@
      * Process device presence stop request.
      */
     public void stopObservingDevicePresence(ObservingDevicePresenceRequest request,
-            String packageName, int userId) {
+            String packageName, int userId, boolean enforcePermissions) {
         Slog.i(TAG,
                 "Stop observing request=[" + request + "] for userId=[" + userId + "], package=["
                         + packageName + "]...");
@@ -244,7 +246,9 @@
         final ParcelUuid requestUuid = request.getUuid();
 
         if (requestUuid != null) {
-            enforceCallerCanObserveDevicePresenceByUuid(mContext);
+            if (enforcePermissions) {
+                enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId);
+            }
 
             if (!mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) {
                 Slog.i(TAG, "UUID=[" + requestUuid + "], package=[" + packageName + "], userId=["
@@ -283,7 +287,7 @@
      * For legacy device presence below Android V.
      *
      * @deprecated Use {@link #startObservingDevicePresence(ObservingDevicePresenceRequest, String,
-     * int)}
+     * int, boolean)}
      */
     @Deprecated
     public void startObservingDevicePresence(int userId, String packageName, String deviceAddress)
@@ -306,14 +310,14 @@
 
         startObservingDevicePresence(
                 new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId())
-                        .build(), packageName, userId);
+                        .build(), packageName, userId, /* enforcePermissions */ true);
     }
 
     /**
      * For legacy device presence below Android V.
      *
      * @deprecated Use {@link #stopObservingDevicePresence(ObservingDevicePresenceRequest, String,
-     * int)}
+     * int, boolean)}
      */
     @Deprecated
     public void stopObservingDevicePresence(int userId, String packageName, String deviceAddress)
@@ -336,7 +340,7 @@
 
         stopObservingDevicePresence(
                 new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId())
-                        .build(), packageName, userId);
+                        .build(), packageName, userId, /* enforcePermissions */ true);
     }
 
     /**
diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index 796d285..c927cd0 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -35,6 +35,8 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.UserHandle.getCallingUserId;
 
+import static com.android.server.companion.utils.RolesUtils.isRoleHolder;
+
 import static java.util.Collections.unmodifiableMap;
 
 import android.Manifest;
@@ -44,6 +46,7 @@
 import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
 import android.content.Context;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.ArrayMap;
@@ -203,11 +206,9 @@
     /**
      * Require the caller to hold necessary permission to observe device presence by UUID.
      */
-    public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context) {
-        if (context.checkCallingPermission(REQUEST_OBSERVE_DEVICE_UUID_PRESENCE)
-                != PERMISSION_GRANTED
-                || context.checkCallingPermission(BLUETOOTH_SCAN) != PERMISSION_GRANTED
-                || context.checkCallingPermission(BLUETOOTH_CONNECT) != PERMISSION_GRANTED) {
+    public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context,
+            String packageName, int userId) {
+        if (!hasRequirePermissions(context, packageName, userId)) {
             throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
                     + "permissions to request observing device presence base on the UUID");
         }
@@ -234,6 +235,17 @@
         return sAppOpsService;
     }
 
+    private static boolean hasRequirePermissions(
+            @NonNull Context context, String packageName, int userId) {
+        return context.checkCallingPermission(
+                REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) == PERMISSION_GRANTED
+                && context.checkCallingPermission(BLUETOOTH_SCAN) == PERMISSION_GRANTED
+                && context.checkCallingPermission(BLUETOOTH_CONNECT) == PERMISSION_GRANTED
+                && Boolean.TRUE.equals(Binder.withCleanCallingIdentity(
+                        () -> isRoleHolder(context, userId, packageName,
+                                DEVICE_PROFILE_AUTOMOTIVE_PROJECTION)));
+    }
+
     // DO NOT USE DIRECTLY! Access via getAppOpsService().
     private static IAppOpsService sAppOpsService = null;
 
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 4a99007..6704049 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1055,7 +1055,7 @@
 
     @Override // Binder call
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-    public int getVirtualCameraId(@NonNull VirtualCameraConfig cameraConfig)
+    public String getVirtualCameraId(@NonNull VirtualCameraConfig cameraConfig)
             throws RemoteException {
         super.getVirtualCameraId_enforcePermission();
         Objects.requireNonNull(cameraConfig);
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 743086e..62efafb 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -135,7 +135,7 @@
     }
 
     /** Return the id of the virtual camera with the given config. */
-    public int getCameraId(@NonNull VirtualCameraConfig cameraConfig) {
+    public String getCameraId(@NonNull VirtualCameraConfig cameraConfig) {
         connectVirtualCameraServiceIfNeeded();
 
         try {
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 2607ed3..89c888c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE;
 import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
+import static android.service.contentcapture.ContentCaptureService.ASSIST_CONTENT_ACTIVITY_START_KEY;
 import static android.service.contentcapture.ContentCaptureService.setClientState;
 import static android.view.contentcapture.ContentCaptureHelper.toList;
 import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS;
@@ -28,6 +29,7 @@
 import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD;
 import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG;
 import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER;
+import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT;
 import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
 import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK;
 import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION;
@@ -44,6 +46,7 @@
 import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED;
 import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST;
 import static com.android.internal.util.SyncResultReceiver.bundleFor;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -52,10 +55,12 @@
 import android.app.ActivityThread;
 import android.app.admin.DevicePolicyCache;
 import android.app.assist.ActivityId;
+import android.app.assist.AssistContent;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ActivityPresentationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -184,6 +189,9 @@
     @Nullable
     private boolean mDisabledByDeviceConfig;
 
+    @GuardedBy("mLock")
+    private boolean activityStartAssistDataEnabled;
+
     // Device-config settings that are cached and passed back to apps
     @GuardedBy("mLock")
     int mDevCfgLoggingLevel;
@@ -451,6 +459,8 @@
                 case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT:
                     setFineTuneParamsFromDeviceConfig();
                     return;
+                case DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT:
+                    setActivityStartAssistDataEnabled();
                 default:
                     Slog.i(TAG, "Ignoring change on " + key);
             }
@@ -639,6 +649,15 @@
         final String enabled = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                 ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED);
         setDisabledByDeviceConfig(enabled);
+        setActivityStartAssistDataEnabled();
+    }
+
+    private void setActivityStartAssistDataEnabled() {
+        synchronized (mLock) {
+            this.activityStartAssistDataEnabled = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+                    DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT, false);
+        }
     }
 
     private void setDisabledByDeviceConfig(@Nullable String explicitlyEnabled) {
@@ -908,6 +927,9 @@
         pw.print(prefix2);
         pw.print("contentProtectionAutoDisconnectTimeoutMs: ");
         pw.println(mDevCfgContentProtectionAutoDisconnectTimeoutMs);
+        pw.print(prefix2);
+        pw.print("activityStartAssistDataEnabled: ");
+        pw.println(activityStartAssistDataEnabled);
         pw.print(prefix);
         pw.println("Global Options:");
         mGlobalContentCaptureOptions.dump(prefix2, pw);
@@ -1327,6 +1349,33 @@
         }
 
         @Override
+        @SuppressWarnings("GuardedBy")
+        public boolean sendActivityStartAssistData(@UserIdInt int userId,
+                @NonNull IBinder activityToken,
+                @NonNull Intent intentData) {
+            synchronized (mLock) {
+                if (!activityStartAssistDataEnabled) {
+                    return false;
+                }
+                Intent intent = new Intent(intentData);
+                intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                        | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
+                Bundle assistContentExtras = new Bundle();
+                assistContentExtras.putBoolean(ASSIST_CONTENT_ACTIVITY_START_KEY, true);
+                AssistContent assistContent = new AssistContent(assistContentExtras);
+                assistContent.setDefaultIntent(intent);
+
+                final Bundle activityAssistData = new Bundle();
+                activityAssistData.putParcelable(ASSIST_KEY_CONTENT, assistContent);
+                final ContentCapturePerUserService service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    return service.sendActivityAssistDataLocked(activityToken, activityAssistData);
+                }
+            }
+            return false;
+        }
+
+        @Override
         public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken,
                 @NonNull Bundle data) {
             synchronized (mLock) {
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index bc35fea..b64aa8a 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -47,6 +47,7 @@
 import android.app.contextualsearch.ContextualSearchState;
 import android.app.contextualsearch.IContextualSearchCallback;
 import android.app.contextualsearch.IContextualSearchManager;
+import android.app.contextualsearch.flags.Flags;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -66,6 +67,7 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
 import android.view.IWindowManager;
@@ -91,6 +93,8 @@
     private static final String TAG = ContextualSearchManagerService.class.getSimpleName();
     private static final int MSG_RESET_TEMPORARY_PACKAGE = 0;
     private static final int MAX_TEMP_PACKAGE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+    private static final int MSG_INVALIDATE_TOKEN = 1;
+    private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes
 
     private final Context mContext;
     private final ActivityTaskManagerInternal mAtmInternal;
@@ -145,6 +149,8 @@
     private Handler mTemporaryHandler;
     @GuardedBy("this")
     private String mTemporaryPackage = null;
+    @GuardedBy("this")
+    private long mTokenValidDurationMs = MAX_TOKEN_VALID_DURATION_MS;
 
     @GuardedBy("mLock")
     private IContextualSearchCallback mStateCallback;
@@ -163,6 +169,8 @@
                 IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)),
                 mContext.getSystemService(AppOpsManager.class),
                 mAssistDataCallbacks, mLock, OP_ASSIST_STRUCTURE, OP_ASSIST_SCREENSHOT);
+
+        updateSecureSetting();
     }
 
     @Override
@@ -170,6 +178,22 @@
         publishBinderService(CONTEXTUAL_SEARCH_SERVICE, new ContextualSearchManagerStub());
     }
 
+    private void updateSecureSetting() {
+        // Write default package to secure setting every time there is a change. If OEM didn't
+        // supply a new value in their config, then we would write empty string.
+        Settings.Secure.putString(
+            mContext.getContentResolver(),
+            Settings.Secure.CONTEXTUAL_SEARCH_PACKAGE,
+            getContextualSearchPackageName());
+    }
+
+    private String getContextualSearchPackageName() {
+      synchronized (this) {
+         return mTemporaryPackage != null ? mTemporaryPackage : mContext
+                .getResources().getString(R.string.config_defaultContextualSearchPackageName);
+      }
+    }
+
     void resetTemporaryPackage() {
         synchronized (this) {
             enforceOverridingPermission("resetTemporaryPackage");
@@ -179,6 +203,7 @@
             }
             if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage reset.");
             mTemporaryPackage = null;
+            updateSecureSetting();
         }
     }
 
@@ -207,16 +232,39 @@
                 mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE);
             }
             mTemporaryPackage = temporaryPackage;
+            updateSecureSetting();
             mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_PACKAGE, durationMs);
             if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage set to " + mTemporaryPackage);
         }
     }
 
+    void resetTokenValidDurationMs() {
+        setTokenValidDurationMs(MAX_TOKEN_VALID_DURATION_MS);
+    }
+
+    void setTokenValidDurationMs(int durationMs) {
+        synchronized (this) {
+            enforceOverridingPermission("setTokenValidDurationMs");
+            if (durationMs > MAX_TOKEN_VALID_DURATION_MS) {
+                throw new IllegalArgumentException(
+                        "Token max duration is " + MAX_TOKEN_VALID_DURATION_MS + " (called with "
+                                + durationMs + ")");
+            }
+            mTokenValidDurationMs = durationMs;
+            if (DEBUG_USER) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs);
+        }
+    }
+
+    private long getTokenValidDurationMs() {
+        synchronized (this) {
+            return mTokenValidDurationMs;
+        }
+    }
+
     private Intent getResolvedLaunchIntent() {
         synchronized (this) {
             // If mTemporaryPackage is not null, use it to get the ContextualSearch intent.
-            String csPkgName = mTemporaryPackage != null ? mTemporaryPackage : mContext
-                    .getResources().getString(R.string.config_defaultContextualSearchPackageName);
+            String csPkgName = getContextualSearchPackageName();
             if (csPkgName.isEmpty()) {
                 // Return null if csPackageName is not specified.
                 return null;
@@ -356,15 +404,51 @@
     }
 
     private class ContextualSearchManagerStub extends IContextualSearchManager.Stub {
+        @GuardedBy("this")
+        private Handler mTokenHandler;
         private @Nullable CallbackToken mToken;
 
+        private void invalidateToken() {
+            synchronized (this) {
+                if (mTokenHandler != null) {
+                    mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN);
+                    mTokenHandler = null;
+                }
+                if (DEBUG_USER) Log.d(TAG, "mToken invalidated.");
+                mToken = null;
+            }
+        }
+
+        private void issueToken() {
+            synchronized (this) {
+                mToken = new CallbackToken();
+                if (mTokenHandler == null) {
+                    mTokenHandler = new Handler(Looper.getMainLooper(), null, true) {
+                        @Override
+                        public void handleMessage(Message msg) {
+                            if (msg.what == MSG_INVALIDATE_TOKEN) {
+                                invalidateToken();
+                            } else {
+                                Slog.wtf(TAG, "invalid token handler msg: " + msg);
+                            }
+                        }
+                    };
+                } else {
+                    mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN);
+                }
+                mTokenHandler.sendEmptyMessageDelayed(
+                        MSG_INVALIDATE_TOKEN, getTokenValidDurationMs());
+            }
+        }
+
         @Override
         public void startContextualSearch(int entrypoint) {
             synchronized (this) {
                 if (DEBUG_USER) Log.d(TAG, "startContextualSearch");
                 enforcePermission("startContextualSearch");
                 mAssistDataRequester.cancel();
-                mToken = new CallbackToken();
+                // Creates a new CallbackToken at mToken and an expiration handler.
+                issueToken();
                 // We get the launch intent with the system server's identity because the system
                 // server has READ_FRAME_BUFFER permission to get the screenshot and because only
                 // the system server can invoke non-exported activities.
@@ -397,7 +481,31 @@
                 }
                 return;
             }
-            mToken = null;
+            invalidateToken();
+            if (Flags.enableTokenRefresh()) {
+                issueToken();
+                Bundle bundle = new Bundle();
+                bundle.putParcelable(ContextualSearchManager.EXTRA_TOKEN, mToken);
+                // We get take the screenshot with the system server's identity because the system
+                // server has READ_FRAME_BUFFER permission to get the screenshot.
+                Binder.withCleanCallingIdentity(() -> {
+                    if (mWmInternal != null) {
+                        bundle.putParcelable(ContextualSearchManager.EXTRA_SCREENSHOT,
+                                mWmInternal.takeAssistScreenshot(Set.of(
+                                        TYPE_STATUS_BAR,
+                                        TYPE_NAVIGATION_BAR,
+                                        TYPE_NAVIGATION_BAR_PANEL,
+                                        TYPE_POINTER))
+                                .asBitmap().asShared());
+                    }
+                    try {
+                        callback.onResult(
+                            new ContextualSearchState(null, null, bundle));
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error invoking ContextualSearchCallback", e);
+                    }
+                });
+            }
             synchronized (mLock) {
                 mStateCallback = callback;
             }
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
index 5777e1d..66a4e7b 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
@@ -52,6 +52,19 @@
                                 + packageName + " for " + duration + "ms");
                         break;
                     }
+                    case "token-duration": {
+                        String durationStr = getNextArg();
+                        if (durationStr == null) {
+                            mService.resetTokenValidDurationMs();
+                            pw.println("ContextualSearchManagerService token duration reset.");
+                            return 0;
+                        }
+                        final int durationMs = Integer.parseInt(durationStr);
+                        mService.setTokenValidDurationMs(durationMs);
+                        pw.println("ContextualSearchManagerService temporarily set token duration"
+                                + " to " + durationMs + "ms");
+                        break;
+                    }
                 }
             }
             break;
@@ -72,6 +85,9 @@
             pw.println("    Temporarily (for DURATION ms) changes the Contextual Search "
                     + "implementation.");
             pw.println("    To reset, call without any arguments.");
+            pw.println("  set token-duration [DURATION]");
+            pw.println("    Changes the Contextual Search token duration to DURATION ms.");
+            pw.println("    To reset, call without any arguments.");
             pw.println("");
         }
     }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 167c384..53730e3 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -227,6 +227,7 @@
         "connectivity_flags_lib",
         "dreams_flags_lib",
         "aconfig_new_storage_flags_lib",
+        "powerstats_flags_lib",
     ],
     javac_shard_size: 50,
     javacflags: [
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 2545620..c4d38e4 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
 # BootReceiver / Watchdog
-per-file BootReceiver.java,Watchdog.java = gaillard@google.com
+per-file BootReceiver.java,Watchdog.java = benmiles@google.com
 
 # Connectivity / Networking
 per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java,VpnManagerService.java = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 966478e..eb03709 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -663,6 +663,7 @@
                      PackageHealthObserverImpact.USER_IMPACT_LEVEL_10,
                      PackageHealthObserverImpact.USER_IMPACT_LEVEL_20,
                      PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
+                     PackageHealthObserverImpact.USER_IMPACT_LEVEL_40,
                      PackageHealthObserverImpact.USER_IMPACT_LEVEL_50,
                      PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
                      PackageHealthObserverImpact.USER_IMPACT_LEVEL_71,
@@ -678,6 +679,7 @@
         /* Actions having medium user impact, user of a device will likely notice. */
         int USER_IMPACT_LEVEL_20 = 20;
         int USER_IMPACT_LEVEL_30 = 30;
+        int USER_IMPACT_LEVEL_40 = 40;
         int USER_IMPACT_LEVEL_50 = 50;
         int USER_IMPACT_LEVEL_70 = 70;
         /* Action has high user impact, a last resort, user of a device will be very frustrated. */
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 37c2d26..189b2495 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -673,7 +673,7 @@
                 case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET:
                     return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10;
                 case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET:
-                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_20;
+                    return PackageHealthObserverImpact.USER_IMPACT_LEVEL_40;
                 case RESCUE_LEVEL_WARM_REBOOT:
                     return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50;
                 case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS:
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 8c1bb3b..44aea15 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -352,7 +352,7 @@
     @NonNull private final Set<String> mInitialNonStoppedSystemPackages = new ArraySet<>();
 
     // Which packages (key) are allowed to join particular SharedUid (value).
-    @NonNull private final Map<String, String> mPackageToSharedUidAllowList = new ArrayMap<>();
+    @NonNull private final ArrayMap<String, String> mPackageToSharedUidAllowList = new ArrayMap<>();
 
     // A map of preloaded package names and the path to its app metadata file path.
     private final ArrayMap<String, String> mAppMetadataFilePaths = new ArrayMap<>();
@@ -574,7 +574,7 @@
     }
 
     @NonNull
-    public Map<String, String> getPackageToSharedUidAllowList() {
+    public ArrayMap<String, String> getPackageToSharedUidAllowList() {
         return mPackageToSharedUidAllowList;
     }
 
@@ -720,6 +720,9 @@
         }
         // Read configuration of features, libs and priv-app permissions from apex module.
         int apexPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
+        if (android.permission.flags.Flags.apexSignaturePermissionAllowlistEnabled()) {
+            apexPermissionFlag |= ALLOW_SIGNATURE_PERMISSIONS;
+        }
         // TODO: Use a solid way to filter apex module folders?
         for (File f: FileUtils.listFilesOrEmpty(Environment.getApexDirectory())) {
             if (f.isFile() || f.getPath().contains("@")) {
@@ -1322,6 +1325,8 @@
                                     Environment.getProductDirectory().toPath() + "/");
                             boolean systemExt = permFile.toPath().startsWith(
                                     Environment.getSystemExtDirectory().toPath() + "/");
+                            boolean apex = permFile.toPath().startsWith(
+                                    Environment.getApexDirectory().toPath() + "/");
                             if (vendor) {
                                 readSignatureAppPermissions(parser,
                                         mPermissionAllowlist.getVendorSignatureAppAllowlist());
@@ -1331,6 +1336,9 @@
                             } else if (systemExt) {
                                 readSignatureAppPermissions(parser,
                                         mPermissionAllowlist.getSystemExtSignatureAppAllowlist());
+                            } else if (apex) {
+                                readSignatureAppPermissions(parser,
+                                        mPermissionAllowlist.getApexSignatureAppAllowlist());
                             } else {
                                 readSignatureAppPermissions(parser,
                                         mPermissionAllowlist.getSignatureAppAllowlist());
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index f1d3584..1c13ad5 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -32,6 +32,7 @@
 import static android.app.UiModeManager.PROJECTION_TYPE_NONE;
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.os.UserHandle.getCallingUserId;
+import static android.os.UserManager.isVisibleBackgroundUsersEnabled;
 import static android.provider.Settings.Secure.CONTRAST_LEVEL;
 import static android.util.TimeUtils.isTimeBetween;
 
@@ -99,6 +100,7 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.DumpUtils;
+import com.android.server.pm.UserManagerService;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
@@ -848,6 +850,8 @@
             }
 
             final int user = UserHandle.getCallingUserId();
+            enforceValidCallingUser(user);
+
             final long ident = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -910,6 +914,8 @@
                 @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) {
             setAttentionModeThemeOverlay_enforcePermission();
 
+            enforceValidCallingUser(UserHandle.getCallingUserId());
+
             synchronized (mLock) {
                 if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) {
                     mAttentionModeThemeOverlay = attentionModeThemeOverlayType;
@@ -999,6 +1005,8 @@
                 return false;
             }
             final int user = Binder.getCallingUserHandle().getIdentifier();
+            enforceValidCallingUser(user);
+
             if (user != mCurrentUser && getContext().checkCallingOrSelfPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -1056,6 +1064,8 @@
                 return;
             }
             final int user = UserHandle.getCallingUserId();
+            enforceValidCallingUser(user);
+
             final long ident = Binder.clearCallingIdentity();
             try {
                 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
@@ -1084,6 +1094,8 @@
                 return;
             }
             final int user = UserHandle.getCallingUserId();
+            enforceValidCallingUser(user);
+
             final long ident = Binder.clearCallingIdentity();
             try {
                 LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000);
@@ -1104,6 +1116,8 @@
             assertLegit(callingPackage);
             assertSingleProjectionType(projectionType);
             enforceProjectionTypePermissions(projectionType);
+            enforceValidCallingUser(getCallingUserId());
+
             synchronized (mLock) {
                 if (mProjectionHolders == null) {
                     mProjectionHolders = new SparseArray<>(1);
@@ -1148,6 +1162,8 @@
             assertLegit(callingPackage);
             assertSingleProjectionType(projectionType);
             enforceProjectionTypePermissions(projectionType);
+            enforceValidCallingUser(getCallingUserId());
+
             return releaseProjectionUnchecked(projectionType, callingPackage);
         }
 
@@ -1187,6 +1203,9 @@
             if (projectionType == PROJECTION_TYPE_NONE) {
                 return;
             }
+
+            enforceValidCallingUser(getCallingUserId());
+
             synchronized (mLock) {
                 if (mProjectionListeners == null) {
                     mProjectionListeners = new SparseArray<>(1);
@@ -1234,6 +1253,32 @@
         }
     };
 
+    // This method validates whether calling user is valid in visible background users
+    // feature. Valid user is the current user or the system or in the same profile group as
+    // the current user.
+    private void enforceValidCallingUser(int userId) {
+        if (!isVisibleBackgroundUsersEnabled()) {
+            return;
+        }
+        if (LOG) {
+            Slog.d(TAG, "enforceValidCallingUser: userId=" + userId
+                    + " isSystemUser=" + (userId == USER_SYSTEM) + " current user=" + mCurrentUser
+                    + " callingPid=" + Binder.getCallingPid()
+                    + " callingUid=" + mInjector.getCallingUid());
+        }
+        long ident = Binder.clearCallingIdentity();
+        try {
+            if (userId != USER_SYSTEM && userId != mCurrentUser
+                    && !UserManagerService.getInstance().isSameProfileGroup(userId, mCurrentUser)) {
+                throw new SecurityException(
+                        "Calling user is not valid for level-1 compatibility in MUMD. "
+                                + "callingUserId=" + userId + " currentUserId=" + mCurrentUser);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private void enforceProjectionTypePermissions(@UiModeManager.ProjectionType int p) {
         if ((p & PROJECTION_TYPE_AUTOMOTIVE) != 0) {
             getContext().enforceCallingPermission(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b02a4e4..6bd7445 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -775,7 +775,8 @@
         this.mFGSLogger = new ForegroundServiceTypeLoggerModule();
         this.mActiveServiceAnrTimer = new ProcessAnrTimer(service,
                 ActivityManagerService.SERVICE_TIMEOUT_MSG,
-                "SERVICE_TIMEOUT");
+                "SERVICE_TIMEOUT",
+                new AnrTimer.Args().freeze(true));
         this.mShortFGSAnrTimer = new ServiceAnrTimer(service,
                 ActivityManagerService.SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG,
                 "SHORT_FGS_TIMEOUT");
@@ -830,7 +831,8 @@
             for (int i = 0; i < smap.mServicesByInstanceName.size(); i++) {
                 final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
                 if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) {
-                    if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) {
+                    if (sr.foregroundNoti != null
+                            && Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) {
                         if (DEBUG_FOREGROUND_SERVICE) {
                             Slog.d(TAG_SERVICE, "Channel u" + userId + "/pkg=" + pkg
                                     + "/channelId=" + channelId
@@ -3891,9 +3893,10 @@
                 return;
             }
 
-            final long lastTopTime = sr.app.mState.getLastTopTime();
-            final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
+            final boolean currentlyTop = sr.app.mState.getCurProcState() <= PROCESS_STATE_TOP;
             final long nowUptime = SystemClock.uptimeMillis();
+            final long lastTopTime = currentlyTop ? nowUptime : sr.app.mState.getLastTopTime();
+            final long constantTimeLimit = getTimeLimitForFgsType(fgsType);
             if (lastTopTime != Long.MIN_VALUE && constantTimeLimit > (nowUptime - lastTopTime)) {
                 // Discard any other messages for this service
                 mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
@@ -6289,7 +6292,7 @@
                 final ComponentName clientSideComponentName =
                         cr.aliasComponent != null ? cr.aliasComponent : r.name;
                 try {
-                    cr.conn.connected(r.name, null, true);
+                    cr.conn.connected(clientSideComponentName, null, true);
                 } catch (Exception e) {
                     Slog.w(TAG, "Failure disconnecting service " + r.shortInstanceName
                           + " to connection " + c.get(i).conn.asBinder()
@@ -7519,7 +7522,7 @@
                     }
                 }
                 if (timeout != null && mAm.mProcessList.isInLruListLOSP(proc)) {
-                    mActiveServiceAnrTimer.accept(proc);
+                    final AutoCloseable timer = mActiveServiceAnrTimer.accept(proc);
                     Slog.w(TAG, "Timeout executing service: " + timeout);
                     StringWriter sw = new StringWriter();
                     PrintWriter pw = new FastPrintWriter(sw, false, 1024);
@@ -7532,7 +7535,7 @@
                             LAST_ANR_LIFETIME_DURATION_MSECS);
                     long waitedMillis = now - timeout.executingStart;
                     timeoutRecord = TimeoutRecord.forServiceExec(timeout.shortInstanceName,
-                            waitedMillis);
+                            waitedMillis).setExpiredTimer(timer);
                 } else {
                     mActiveServiceAnrTimer.discard(proc);
                     final long delay = psr.shouldExecServicesFg()
@@ -7637,6 +7640,11 @@
             super(Objects.requireNonNull(am).mHandler, msg, label);
         }
 
+        ProcessAnrTimer(ActivityManagerService am, int msg, String label,
+                @NonNull AnrTimer.Args args) {
+            super(Objects.requireNonNull(am).mHandler, msg, label, args);
+        }
+
         @Override
         public int getPid(@NonNull ProcessRecord proc) {
             return proc.getPid();
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 26aa053..f5a297b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -169,6 +169,17 @@
      */
     static final String KEY_ENABLE_NEW_OOMADJ = "enable_new_oom_adj";
 
+    /**
+     * Whether or not to enable the batching of OOM adjuster calls to LMKD
+     */
+    static final String KEY_ENABLE_BATCHING_OOM_ADJ = "enable_batching_oom_adj";
+
+    /**
+     * How long to wait before scheduling another follow-up oomAdjuster update for time based state.
+     */
+    static final String KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION =
+            "follow_up_oomadj_update_wait_duration";
+
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024;
     private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
     private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -231,7 +242,7 @@
 
     static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60 * 1000;
     static final long DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS = 60 * 1000;
-    static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = true;
+    static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = false;
 
     static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000;
 
@@ -244,6 +255,16 @@
     private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
 
     /**
+     * The default value to {@link #KEY_ENABLE_BATCHING_OOM_ADJ}.
+     */
+    private static final boolean DEFAULT_ENABLE_BATCHING_OOM_ADJ = Flags.batchingOomAdj();
+
+    /**
+     * The default value to {@link #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION}.
+     */
+    private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L;
+
+    /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
      */
     private static final int
@@ -1136,6 +1157,13 @@
     /** @see #KEY_ENABLE_NEW_OOMADJ */
     public boolean ENABLE_NEW_OOMADJ = DEFAULT_ENABLE_NEW_OOM_ADJ;
 
+    /** @see #KEY_ENABLE_BATCHING_OOM_ADJ */
+    public boolean ENABLE_BATCHING_OOM_ADJ = DEFAULT_ENABLE_BATCHING_OOM_ADJ;
+
+    /** @see #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION */
+    public long FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION =
+            DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION;
+
     /**
      * Indicates whether PSS profiling in AppProfiler is disabled or not.
      */
@@ -1346,6 +1374,9 @@
                             case KEY_PROC_STATE_DEBUG_UIDS:
                                 updateProcStateDebugUids();
                                 break;
+                            case KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION:
+                                updateFollowUpOomAdjUpdateWaitDuration();
+                                break;
                             default:
                                 updateFGSPermissionEnforcementFlagsIfNecessary(name);
                                 break;
@@ -1479,6 +1510,8 @@
     private void loadNativeBootDeviceConfigConstants() {
         ENABLE_NEW_OOMADJ = getDeviceConfigBoolean(KEY_ENABLE_NEW_OOMADJ,
                 DEFAULT_ENABLE_NEW_OOM_ADJ);
+        ENABLE_BATCHING_OOM_ADJ = getDeviceConfigBoolean(KEY_ENABLE_BATCHING_OOM_ADJ,
+                DEFAULT_ENABLE_BATCHING_OOM_ADJ);
     }
 
     public void setOverrideMaxCachedProcesses(int value) {
@@ -2231,6 +2264,13 @@
             DEFAULT_ENABLE_NEW_OOM_ADJ);
     }
 
+    private void updateFollowUpOomAdjUpdateWaitDuration() {
+        FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION,
+                DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION);
+    }
+
     private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) {
         ForegroundServiceTypePolicy.getDefaultPolicy()
             .updatePermissionEnforcementFlagIfNecessary(name);
@@ -2248,6 +2288,13 @@
                 mDefaultPssToRssThresholdModifier);
     }
 
+    private void updateEnableBatchingOomAdj() {
+        ENABLE_BATCHING_OOM_ADJ = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+                KEY_ENABLE_BATCHING_OOM_ADJ,
+                DEFAULT_ENABLE_BATCHING_OOM_ADJ);
+    }
+
     boolean shouldDebugUidForProcState(int uid) {
         SparseBooleanArray ar = mProcStateDebugUids;
         final var size = ar.size();
@@ -2476,6 +2523,9 @@
         pw.print("  "); pw.print(KEY_MAX_PREVIOUS_TIME);
         pw.print("="); pw.println(MAX_PREVIOUS_TIME);
 
+        pw.print("  "); pw.print(KEY_ENABLE_BATCHING_OOM_ADJ);
+        pw.print("="); pw.println(ENABLE_BATCHING_OOM_ADJ);
+
         pw.println();
         if (mOverrideMaxCachedProcesses >= 0) {
             pw.print("  mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);
@@ -2489,6 +2539,9 @@
         pw.print("  ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION=");
         pw.println(mEnableWaitForFinishAttachApplication);
 
+        pw.print("  "); pw.print(KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION);
+        pw.print("="); pw.println(FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION);
+
         synchronized (mProcStateDebugUids) {
             pw.print("  "); pw.print(KEY_PROC_STATE_DEBUG_UIDS);
             pw.print("="); pw.println(mProcStateDebugUids);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2600f10..d41de38 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -306,6 +306,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.TestUtilityService;
 import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
 import android.content.pm.VersionedPackage;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -1663,6 +1664,7 @@
     static final int BIND_APPLICATION_TIMEOUT_HARD_MSG = 83;
     static final int SERVICE_FGS_TIMEOUT_MSG = 84;
     static final int SERVICE_FGS_CRASH_TIMEOUT_MSG = 85;
+    static final int FOLLOW_UP_OOMADJUSTER_UPDATE_MSG = 86;
 
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
 
@@ -2036,6 +2038,9 @@
                 case SERVICE_FGS_CRASH_TIMEOUT_MSG: {
                     mServices.onFgsCrashTimeout((ServiceRecord) msg.obj);
                 } break;
+                case FOLLOW_UP_OOMADJUSTER_UPDATE_MSG: {
+                    handleFollowUpOomAdjusterUpdate();
+                } break;
             }
         }
     }
@@ -3933,11 +3938,28 @@
                                 + packageName + ": " + e);
                     }
                     if (mUserController.isUserRunning(user, userRunningFlags)) {
+
+                        String description;
+                        if (reason == null) {
+                            description = "from pid " + callingPid;
+
+                            // Add the name of the process if it's available
+                            final ProcessRecord callerApp;
+                            synchronized (mPidsSelfLocked) {
+                                callerApp = mPidsSelfLocked.get(callingPid);
+                            }
+                            if (callerApp != null) {
+                                description += " (" + callerApp.processName + ")";
+                            }
+                        } else {
+                            description = reason;
+                        }
+
                         forceStopPackageLocked(packageName, UserHandle.getAppId(pkgUid),
                                 false /* callerWillRestart */, false /* purgeCache */,
                                 true /* doIt */, false /* evenPersistent */,
-                                false /* uninstalling */, true /* packageStateStopped */, user,
-                                reason == null ? ("from pid " + callingPid) : reason);
+                                false /* uninstalling */, true /* packageStateStopped */,
+                                user, description);
                         finishForceStopPackageLocked(packageName, pkgUid);
                     }
                 }
@@ -5086,6 +5108,15 @@
         mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage));
     }
 
+    private void handleFollowUpOomAdjusterUpdate() {
+        // Remove any existing duplicate messages on the handler here while no lock is being held.
+        // If another follow up update is needed, it will be scheduled by OomAdjuster.
+        mHandler.removeMessages(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG);
+        synchronized (this) {
+            mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        }
+    }
+
     /**
      * @return The last part of the string of an intent's action.
      */
@@ -10207,6 +10238,19 @@
         addStartInfoTimestampInternal(key, timestampNs, userId, callingUid);
     }
 
+    @Override
+    public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs,
+            long framePresentedTimeNs) {
+        int callingUid = Binder.getCallingUid();
+        int userId = UserHandle.getUserId(callingUid);
+        addStartInfoTimestampInternal(
+                ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME,
+                renderThreadDrawStartTimeNs, userId, callingUid);
+        addStartInfoTimestampInternal(
+                ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE,
+                framePresentedTimeNs, userId, callingUid);
+    }
+
     private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) {
         mProcessList.getAppStartInfoTracker().addTimestampToStart(
                 Settings.getPackageNameForUid(mContext, uid),
@@ -10424,11 +10468,6 @@
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
             ResultReceiver resultReceiver) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != ROOT_UID && callingUid != Process.SHELL_UID) {
-            resultReceiver.send(-1, null);
-            throw new SecurityException("Shell commands are only callable by root or shell");
-        }
         (new ActivityManagerShellCommand(this, false)).exec(
                 this, in, out, err, args, callback, resultReceiver);
     }
@@ -14604,7 +14643,7 @@
             final StringBuilder sb = new StringBuilder("registerReceiver: ");
             sb.append(Binder.getCallingUid()); sb.append('/');
             sb.append(receiverId == null ? "null" : receiverId); sb.append('/');
-            final int actionsCount = filter.countActions();
+            final int actionsCount = filter.safeCountActions();
             if (actionsCount > 0) {
                 for (int i = 0; i < actionsCount; ++i) {
                     sb.append(filter.getAction(i));
@@ -15266,15 +15305,50 @@
             BackgroundStartPrivileges backgroundStartPrivileges,
             @Nullable int[] broadcastAllowList,
             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
-        final int cookie = BroadcastQueue.traceBegin("broadcastIntentLockedTraced");
-        final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId,
-                intent, resolvedType, resultToApp, resultTo, resultCode, resultData, resultExtras,
-                requiredPermissions, excludedPermissions, excludedPackages, appOp,
-                BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky,
-                callingPid, callingUid, realCallingUid, realCallingPid, userId,
-                backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver);
-        BroadcastQueue.traceEnd(cookie);
-        return res;
+        final int cookie = traceBroadcastIntentBegin(intent, resultTo, ordered, sticky,
+                callingUid, realCallingUid, userId);
+        try {
+            final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId,
+                    intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
+                    resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
+                    appOp, BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky,
+                    callingPid, callingUid, realCallingUid, realCallingPid, userId,
+                    backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver);
+            return res;
+        } finally {
+            traceBroadcastIntentEnd(cookie);
+        }
+    }
+
+    private static int traceBroadcastIntentBegin(Intent intent, IIntentReceiver resultTo,
+            boolean ordered, boolean sticky, int callingUid, int realCallingUid, int userId) {
+        if (!Flags.traceReceiverRegistration()) {
+            return BroadcastQueue.traceBegin("broadcastIntentLockedTraced");
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            final StringBuilder sb = new StringBuilder("broadcastIntent: ");
+            sb.append(callingUid); sb.append('/');
+            final String action = intent.getAction();
+            sb.append(action == null ? null : action); sb.append('/');
+            sb.append("0x"); sb.append(Integer.toHexString(intent.getFlags())); sb.append('/');
+            sb.append(ordered ? "O" : "_");
+            sb.append(sticky ? "S" : "_");
+            sb.append(resultTo != null ? "C" : "_");
+            sb.append('/');
+            sb.append('u'); sb.append(userId);
+            if (callingUid != realCallingUid) {
+                sb.append('/');
+                sb.append("sender="); sb.append(realCallingUid);
+            }
+            return BroadcastQueue.traceBegin(sb.toString());
+        }
+        return 0;
+    }
+
+    private static void traceBroadcastIntentEnd(int cookie) {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            BroadcastQueue.traceEnd(cookie);
+        }
     }
 
     @GuardedBy("this")
@@ -18136,7 +18210,9 @@
 
     /**
      * Stops user but allow delayed locking. Delayed locking keeps user unlocked even after
-     * stopping only if {@code config_multiuserDelayUserDataLocking} overlay is set true.
+     * stopping only if {@code config_multiuserDelayUserDataLocking} overlay is set true on the
+     * device or if the user has {@link UserProperties#getAllowStoppingUserWithDelayedLocking()}
+     * set to true.
      *
      * <p>When delayed locking is not enabled through the overlay, this call becomes the same
      * with {@link #stopUserWithCallback(int, IStopUserCallback)} call.
@@ -18148,8 +18224,6 @@
      *         other {@code ActivityManager#USER_OP_*} codes for failure.
      *
      */
-    // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows
-    // delayed locking behavior once the private space flag is finalized.
     @Override
     public int stopUserWithDelayedLocking(@UserIdInt int userId, IStopUserCallback callback) {
         return mUserController.stopUser(userId, /* allowDelayedLocking= */ true,
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index a8227fa..4a7ad31 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -89,6 +89,14 @@
 
     @VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;
 
+    /**
+     * The max number of records that can be present in {@link mInProgressRecords}.
+     *
+     * The magic number of 5 records is expected to be enough because this covers in progress
+     * activity starts only, of which more than a 1-2 at a time is very uncommon/unlikely.
+     */
+    @VisibleForTesting static final int MAX_IN_PROGRESS_RECORDS = 5;
+
     private static final int APP_START_INFO_MONITORING_MODE_LIST_SIZE = 100;
 
     @VisibleForTesting static final String APP_START_STORE_DIR = "procstartstore";
@@ -147,7 +155,6 @@
     /** The path to the historical proc start info file, persisted in the storage. */
     @VisibleForTesting File mProcStartInfoFile;
 
-
     /**
      * Temporary list of records that have not been completed.
      *
@@ -155,7 +162,12 @@
      */
     @GuardedBy("mLock")
     @VisibleForTesting
-    final ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>();
+    final ArrayMap<Long, ApplicationStartInfo> mInProgressRecords = new ArrayMap<>();
+
+    /** Temporary list of keys present in {@link mInProgressRecords} for sorting. */
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    final ArrayList<Integer> mTemporaryInProgressIndexes = new ArrayList<>();
 
     AppStartInfoTracker() {
         mCallbacks = new SparseArray<>();
@@ -193,6 +205,60 @@
         });
     }
 
+    /**
+     * Trim in progress records structure to acceptable size. To be called after each time a new
+     * record is added.
+     *
+     * This is necessary both for robustness, as well as because the call to
+     * {@link onReportFullyDrawn} which triggers the removal in the success case is not guaranteed.
+     *
+     * <p class="note"> Note: this is the expected path for removal of in progress records for
+     * successful activity triggered starts that don't report fully drawn. It is *not* only an edge
+     * case.</p>
+     */
+    @GuardedBy("mLock")
+    private void maybeTrimInProgressRecordsLocked() {
+        if (mInProgressRecords.size() <= MAX_IN_PROGRESS_RECORDS) {
+            // Size is acceptable, do nothing.
+            return;
+        }
+
+        // Make sure the temporary list is empty.
+        mTemporaryInProgressIndexes.clear();
+
+        // Populate the list with indexes for size of {@link mInProgressRecords}.
+        for (int i = 0; i < mInProgressRecords.size(); i++) {
+            mTemporaryInProgressIndexes.add(i, i);
+        }
+
+        // Sort the index collection by value of the corresponding key in {@link mInProgressRecords}
+        // from smallest to largest.
+        Collections.sort(mTemporaryInProgressIndexes, (a, b) -> Long.compare(
+                mInProgressRecords.keyAt(a), mInProgressRecords.keyAt(b)));
+
+        if (mTemporaryInProgressIndexes.size() == MAX_IN_PROGRESS_RECORDS + 1) {
+            // Only removing a single record so don't bother sorting again as we don't have to worry
+            // about indexes changing.
+            mInProgressRecords.removeAt(mTemporaryInProgressIndexes.get(0));
+        } else {
+            // Removing more than 1 record, remove the records we want to keep from the list and
+            // then sort again so we can remove in reverse order of indexes.
+            mTemporaryInProgressIndexes.subList(
+                    mTemporaryInProgressIndexes.size() - MAX_IN_PROGRESS_RECORDS,
+                    mTemporaryInProgressIndexes.size()).clear();
+            Collections.sort(mTemporaryInProgressIndexes);
+
+            // Remove all remaining record indexes in reverse order to avoid changing the already
+            // calculated indexes.
+            for (int i = mTemporaryInProgressIndexes.size() - 1; i >= 0; i--) {
+                mInProgressRecords.removeAt(mTemporaryInProgressIndexes.get(i));
+            }
+        }
+
+        // Clear the temorary list.
+        mTemporaryInProgressIndexes.clear();
+    }
+
     void onIntentStarted(@NonNull Intent intent, long timestampNanos) {
         synchronized (mLock) {
             if (!mEnabled) {
@@ -211,7 +277,8 @@
             } else {
                 start.setReason(ApplicationStartInfo.START_REASON_START_ACTIVITY);
             }
-            mInProgRecords.put(timestampNanos, start);
+            mInProgressRecords.put(timestampNanos, start);
+            maybeTrimInProgressRecordsLocked();
         }
     }
 
@@ -220,17 +287,17 @@
             if (!mEnabled) {
                 return;
             }
-            int index = mInProgRecords.indexOfKey(id);
+            int index = mInProgressRecords.indexOfKey(id);
             if (index < 0) {
                 return;
             }
-            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            ApplicationStartInfo info = mInProgressRecords.valueAt(index);
             if (info == null) {
-                mInProgRecords.removeAt(index);
+                mInProgressRecords.removeAt(index);
                 return;
             }
             info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
-            mInProgRecords.removeAt(index);
+            mInProgressRecords.removeAt(index);
         }
     }
 
@@ -239,13 +306,13 @@
             if (!mEnabled) {
                 return;
             }
-            int index = mInProgRecords.indexOfKey(id);
+            int index = mInProgressRecords.indexOfKey(id);
             if (index < 0) {
                 return;
             }
-            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            ApplicationStartInfo info = mInProgressRecords.valueAt(index);
             if (info == null || app == null) {
-                mInProgRecords.removeAt(index);
+                mInProgressRecords.removeAt(index);
                 return;
             }
             info.setStartType((int) temperature);
@@ -254,9 +321,9 @@
             if (newInfo == null) {
                 // newInfo can be null if records are added before load from storage is
                 // complete. In this case the newly added record will be lost.
-                mInProgRecords.removeAt(index);
+                mInProgressRecords.removeAt(index);
             } else {
-                mInProgRecords.setValueAt(index, newInfo);
+                mInProgressRecords.setValueAt(index, newInfo);
             }
         }
     }
@@ -266,17 +333,17 @@
             if (!mEnabled) {
                 return;
             }
-            int index = mInProgRecords.indexOfKey(id);
+            int index = mInProgressRecords.indexOfKey(id);
             if (index < 0) {
                 return;
             }
-            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            ApplicationStartInfo info = mInProgressRecords.valueAt(index);
             if (info == null) {
-                mInProgRecords.removeAt(index);
+                mInProgressRecords.removeAt(index);
                 return;
             }
             info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
-            mInProgRecords.removeAt(index);
+            mInProgressRecords.removeAt(index);
         }
     }
 
@@ -286,13 +353,13 @@
             if (!mEnabled) {
                 return;
             }
-            int index = mInProgRecords.indexOfKey(id);
+            int index = mInProgressRecords.indexOfKey(id);
             if (index < 0) {
                 return;
             }
-            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            ApplicationStartInfo info = mInProgressRecords.valueAt(index);
             if (info == null) {
-                mInProgRecords.removeAt(index);
+                mInProgressRecords.removeAt(index);
                 return;
             }
             info.setLaunchMode(launchMode);
@@ -308,18 +375,18 @@
             if (!mEnabled) {
                 return;
             }
-            int index = mInProgRecords.indexOfKey(id);
+            int index = mInProgressRecords.indexOfKey(id);
             if (index < 0) {
                 return;
             }
-            ApplicationStartInfo info = mInProgRecords.valueAt(index);
+            ApplicationStartInfo info = mInProgressRecords.valueAt(index);
             if (info == null) {
-                mInProgRecords.removeAt(index);
+                mInProgressRecords.removeAt(index);
                 return;
             }
             info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN,
                     timestampNanos);
-            mInProgRecords.removeAt(index);
+            mInProgressRecords.removeAt(index);
         }
     }
 
@@ -964,7 +1031,7 @@
                 mProcStartInfoFile.delete();
             }
             mData.getMap().clear();
-            mInProgRecords.clear();
+            mInProgressRecords.clear();
         }
     }
 
@@ -1128,21 +1195,8 @@
 
             // Records are sorted newest to oldest, grab record at index 0.
             ApplicationStartInfo startInfo = mInfos.get(0);
-            int startupState = startInfo.getStartupState();
 
-            // If startup state is error then don't accept any further timestamps.
-            if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) {
-                if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps.");
-                return;
-            }
-
-            // If startup state is first frame drawn then only accept fully drawn timestamp.
-            if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN
-                    && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully "
-                            + "drawn, not accepting new timestamps.");
-                }
+            if (!isAddTimestampAllowed(startInfo, key, timestampNs)) {
                 return;
             }
 
@@ -1155,6 +1209,55 @@
             }
         }
 
+        private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key,
+                long timestampNs) {
+            int startupState = startInfo.getStartupState();
+
+            // If startup state is error then don't accept any further timestamps.
+            if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) {
+                if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps.");
+                return false;
+            }
+
+            Map<Integer, Long> timestamps = startInfo.getStartupTimestamps();
+
+            if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) {
+                switch (key) {
+                    case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN:
+                        // Allowed, continue to confirm it's not already added.
+                        break;
+                    case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME:
+                        Long firstFrameTimeNs = timestamps
+                                .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME);
+                        if (firstFrameTimeNs == null) {
+                            // This should never happen. State can't be first frame drawn if first
+                            // frame timestamp was not provided.
+                            return false;
+                        }
+
+                        if (timestampNs > firstFrameTimeNs) {
+                            // Initial renderthread frame has to occur before first frame.
+                            return false;
+                        }
+
+                        // Allowed, continue to confirm it's not already added.
+                        break;
+                    case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE:
+                        // Allowed, continue to confirm it's not already added.
+                        break;
+                    default:
+                        return false;
+                }
+            }
+
+            if (timestamps.get(key) != null) {
+                // Timestamp should not occur more than once for a given start.
+                return false;
+            }
+
+            return true;
+        }
+
         @GuardedBy("mLock")
         void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) {
             if (mMonitoringModeEnabled) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d642b02..1ac37ad 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -122,12 +122,16 @@
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.power.optimization.Flags;
 import com.android.server.power.stats.AggregatedPowerStatsConfig;
+import com.android.server.power.stats.AudioPowerStatsProcessor;
 import com.android.server.power.stats.BatteryExternalStatsWorker;
 import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
 import com.android.server.power.stats.BatteryStatsImpl;
 import com.android.server.power.stats.BatteryUsageStatsProvider;
 import com.android.server.power.stats.BluetoothPowerStatsProcessor;
+import com.android.server.power.stats.CameraPowerStatsProcessor;
 import com.android.server.power.stats.CpuPowerStatsProcessor;
+import com.android.server.power.stats.FlashlightPowerStatsProcessor;
+import com.android.server.power.stats.GnssPowerStatsProcessor;
 import com.android.server.power.stats.MobileRadioPowerStatsProcessor;
 import com.android.server.power.stats.PhoneCallPowerStatsProcessor;
 import com.android.server.power.stats.PowerStatsAggregator;
@@ -136,6 +140,7 @@
 import com.android.server.power.stats.PowerStatsStore;
 import com.android.server.power.stats.PowerStatsUidResolver;
 import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+import com.android.server.power.stats.VideoPowerStatsProcessor;
 import com.android.server.power.stats.WifiPowerStatsProcessor;
 import com.android.server.power.stats.wakeups.CpuWakeupStats;
 
@@ -194,7 +199,7 @@
     private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
     private final AtomicFile mConfigFile;
     private final BatteryStats.BatteryStatsDumpHelper mDumpHelper;
-    private final PowerStatsUidResolver mPowerStatsUidResolver;
+    private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
     private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
 
     private volatile boolean mMonitorEnabled = true;
@@ -422,7 +427,6 @@
         setPowerStatsThrottlePeriods(batteryStatsConfigBuilder, context.getResources().getString(
                 com.android.internal.R.string.config_powerStatsThrottlePeriods));
         mBatteryStatsConfig = batteryStatsConfigBuilder.build();
-        mPowerStatsUidResolver = new PowerStatsUidResolver();
         mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
                 systemDir, mHandler, this, this, mUserManagerUserInfoProvider, mPowerProfile,
                 mCpuScalingPolicies, mPowerStatsUidResolver);
@@ -516,6 +520,60 @@
                         AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
                 .setProcessor(
                         new BluetoothPowerStatsProcessor(mPowerProfile));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AUDIO)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(
+                        new AudioPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(new VideoPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(
+                        new FlashlightPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(
+                        new CameraPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(
+                        new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
         return config;
     }
 
@@ -583,6 +641,30 @@
                 BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
                 Flags.streamlinedConnectivityBatteryStats());
 
+        mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_AUDIO,
+                Flags.streamlinedMiscBatteryStats());
+        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+                BatteryConsumer.POWER_COMPONENT_AUDIO,
+                Flags.streamlinedMiscBatteryStats());
+
+        mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_VIDEO,
+                Flags.streamlinedMiscBatteryStats());
+        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+                BatteryConsumer.POWER_COMPONENT_VIDEO,
+                Flags.streamlinedMiscBatteryStats());
+
+        mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
+                Flags.streamlinedMiscBatteryStats());
+        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+                BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
+                Flags.streamlinedMiscBatteryStats());
+
+        mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CAMERA,
+                Flags.streamlinedMiscBatteryStats());
+        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+                BatteryConsumer.POWER_COMPONENT_CAMERA,
+                Flags.streamlinedMiscBatteryStats());
+
         mWorker.systemServicesReady();
         mStats.systemServicesReady(mContext);
         mCpuWakeupStats.systemServicesReady();
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 935282b..178171d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1337,7 +1337,7 @@
         BroadcastAnrTimer(@NonNull Handler handler) {
             super(Objects.requireNonNull(handler),
                     MSG_DELIVERY_TIMEOUT, "BROADCAST_TIMEOUT",
-                new AnrTimer.Args().extend(true));
+                    new AnrTimer.Args().extend(true).freeze(true));
         }
 
         @Override
@@ -1455,11 +1455,12 @@
         if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
             r.anrCount++;
             if (app != null && !app.isDebugging()) {
-                mAnrTimer.accept(queue);
+                final AutoCloseable timer = mAnrTimer.accept(queue);
                 final String packageName = getReceiverPackageName(receiver);
                 final String className = getReceiverClassName(receiver);
-                mService.appNotResponding(queue.app,
-                        TimeoutRecord.forBroadcastReceiver(r.intent, packageName, className));
+                TimeoutRecord tr = TimeoutRecord.forBroadcastReceiver(r.intent, packageName,
+                        className).setExpiredTimer(timer);
+                mService.appNotResponding(queue.app, tr);
             } else {
                 mAnrTimer.discard(queue);
             }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index ab34dd4..c9e0666 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -49,6 +49,7 @@
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FOLLOW_UP;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
@@ -68,9 +69,7 @@
 import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
-import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK;
 import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
-import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL;
 import static android.media.audio.Flags.roForegroundAudioControl;
 import static android.os.Process.THREAD_GROUP_BACKGROUND;
 import static android.os.Process.THREAD_GROUP_DEFAULT;
@@ -91,6 +90,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
 import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG;
+import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG;
 import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG;
 import static com.android.server.am.ActivityManagerService.TAG_BACKUP;
 import static com.android.server.am.ActivityManagerService.TAG_LRU;
@@ -226,6 +226,8 @@
                 return AppProtoEnums.OOM_ADJ_REASON_RESTRICTION_CHANGE;
             case OOM_ADJ_REASON_COMPONENT_DISABLED:
                 return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED;
+            case OOM_ADJ_REASON_FOLLOW_UP:
+                return AppProtoEnums.OOM_ADJ_REASON_FOLLOW_UP;
             default:
                 return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO;
         }
@@ -280,6 +282,8 @@
                 return OOM_ADJ_REASON_METHOD + "_restrictionChange";
             case OOM_ADJ_REASON_COMPONENT_DISABLED:
                 return OOM_ADJ_REASON_METHOD + "_componentDisabled";
+            case OOM_ADJ_REASON_FOLLOW_UP:
+                return OOM_ADJ_REASON_METHOD + "_followUp";
             default:
                 return "_unknown";
         }
@@ -370,6 +374,7 @@
     protected final int[] mTmpSchedGroup = new int[1];
 
     final ActivityManagerService mService;
+    final Injector mInjector;
     final ProcessList mProcessList;
     final ActivityManagerGlobalLock mProcLock;
 
@@ -384,6 +389,13 @@
     protected final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
 
     /**
+     * List of processes that we want to batch for LMKD to adjust their respective
+     * OOM scores.
+     */
+    @GuardedBy("mService")
+    protected final ArrayList<ProcessRecord> mProcsToOomAdj = new ArrayList<ProcessRecord>();
+
+    /**
      * Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate
      * could be called recursively because of the indirect calls during the update;
      * however the oomAdjUpdate itself doesn't support recursion - in this case we'd
@@ -413,12 +425,33 @@
     @GuardedBy("mService")
     protected int mProcessStateCurTop = PROCESS_STATE_TOP;
 
-    /** Overrideable by a test */
+    @GuardedBy("mService")
+    private final ArraySet<ProcessRecord> mFollowUpUpdateSet = new ArraySet<>();
+
+    private static final long NO_FOLLOW_UP_TIME = Long.MAX_VALUE;
+    @GuardedBy("mService")
+    private long mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME;
+
     @VisibleForTesting
-    protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
+    public static class Injector {
+        boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
+                ApplicationInfo app, boolean defaultValue) {
+            return PlatformCompatCache.getInstance()
+                    .isChangeEnabled(cachedCompatChangeId, app, defaultValue);
+        }
+
+        long getUptimeMillis() {
+            return SystemClock.uptimeMillis();
+        }
+
+        long getElapsedRealtimeMillis() {
+            return SystemClock.elapsedRealtime();
+        }
+    }
+
+    boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
             ApplicationInfo app, boolean defaultValue) {
-        return PlatformCompatCache.getInstance()
-                .isChangeEnabled(cachedCompatChangeId, app, defaultValue);
+        return mInjector.isChangeEnabled(cachedCompatChangeId, app, defaultValue);
     }
 
     OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) {
@@ -436,7 +469,18 @@
 
     OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
             ServiceThread adjusterThread) {
+        this(service, processList, activeUids, adjusterThread, new Injector());
+    }
+
+    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
+            Injector injector) {
+        this(service, processList, activeUids, createAdjusterThread(), injector);
+    }
+
+    OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids,
+            ServiceThread adjusterThread, Injector injector) {
         mService = service;
+        mInjector = injector;
         mProcessList = processList;
         mProcLock = service.mProcLock;
         mActiveUids = activeUids;
@@ -454,7 +498,8 @@
                 // Skip setting the process group for system_server, keep it as default.
                 return true;
             }
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            final boolean traceEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            if (traceEnabled) {
                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup "
                         + msg.obj + " to " + group);
             }
@@ -465,7 +510,9 @@
                     Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
                 }
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                if (traceEnabled) {
+                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                }
             }
             return true;
         });
@@ -624,8 +671,8 @@
             // processes, its adj could be still unknown as of now, assign one.
             processes.add(app);
             assignCachedAdjIfNecessary(processes);
-            applyOomAdjLSP(app, false, SystemClock.uptimeMillis(),
-                    SystemClock.elapsedRealtime(), oomAdjReason);
+            applyOomAdjLSP(app, false, mInjector.getUptimeMillis(),
+                    mInjector.getElapsedRealtimeMillis(), oomAdjReason);
         }
         mTmpProcessList.clear();
         mService.clearPendingTopAppLocked();
@@ -842,6 +889,40 @@
     }
 
     @GuardedBy("mService")
+    void updateOomAdjFollowUpTargetsLocked() {
+        final long now = mInjector.getUptimeMillis();
+        long nextFollowUpUptimeMs = Long.MAX_VALUE;
+        mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME;
+        for (int i = mFollowUpUpdateSet.size() - 1; i >= 0; i--) {
+            final ProcessRecord proc = mFollowUpUpdateSet.valueAtUnchecked(i);
+            final long followUpUptimeMs = proc.mState.getFollowupUpdateUptimeMs();
+
+            if (proc.isKilled()) {
+                // Process is dead, just remove from follow up set.
+                mFollowUpUpdateSet.removeAt(i);
+            } else if (followUpUptimeMs <= now) {
+                // Add processes that need a follow up update.
+                mPendingProcessSet.add(proc);
+                proc.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME);
+                mFollowUpUpdateSet.removeAt(i);
+            } else if (followUpUptimeMs < nextFollowUpUptimeMs) {
+                // Figure out when to schedule the next follow up update.
+                nextFollowUpUptimeMs = followUpUptimeMs;
+            } else if (followUpUptimeMs == NO_FOLLOW_UP_TIME) {
+                // The follow up is no longer needed for this process.
+                mFollowUpUpdateSet.removeAt(i);
+            }
+        }
+
+        if (nextFollowUpUptimeMs != Long.MAX_VALUE) {
+            // There is still at least one process that needs a follow up.
+            scheduleFollowUpOomAdjusterUpdateLocked(nextFollowUpUptimeMs, now);
+        }
+
+        updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_FOLLOW_UP);
+    }
+
+    @GuardedBy("mService")
     protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
 
@@ -892,8 +973,8 @@
         if (startProfiling) {
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
         }
-        final long now = SystemClock.uptimeMillis();
-        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long now = mInjector.getUptimeMillis();
+        final long nowElapsed = mInjector.getElapsedRealtimeMillis();
         final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
         final int numProc = activeProcesses.size();
 
@@ -1019,7 +1100,7 @@
         updateUidsLSP(activeUids, nowElapsed);
 
         synchronized (mService.mProcessStats.mLock) {
-            final long nowUptime = SystemClock.uptimeMillis();
+            final long nowUptime = mInjector.getUptimeMillis();
             if (mService.mProcessStats.shouldWriteNowLocked(nowUptime)) {
                 mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
                         mService.mProcessStats));
@@ -1030,7 +1111,7 @@
         }
 
         if (DEBUG_OOM_ADJ) {
-            final long duration = SystemClock.uptimeMillis() - now;
+            final long duration = mInjector.getUptimeMillis() - now;
             if (false) {
                 Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms",
                         new RuntimeException("here").fillInStackTrace());
@@ -1044,7 +1125,7 @@
     protected void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
         final int numLru = lruList.size();
         if (mConstants.USE_TIERED_CACHED_ADJ) {
-            final long now = SystemClock.uptimeMillis();
+            final long now = mInjector.getUptimeMillis();
             for (int i = numLru - 1; i >= 0; i--) {
                 ProcessRecord app = lruList.get(i);
                 final ProcessStateRecord state = app.mState;
@@ -1246,7 +1327,7 @@
             if (!app.isKilledByAm() && app.getThread() != null) {
                 // We don't need to apply the update for the process which didn't get computed
                 if (state.getCompletedAdjSeq() == mAdjSeq) {
-                    applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason);
+                    applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true);
                 }
 
                 if (app.isPendingFinishAttach()) {
@@ -1348,6 +1429,11 @@
             }
         }
 
+        if (!mProcsToOomAdj.isEmpty()) {
+            ProcessList.batchSetOomAdj(mProcsToOomAdj);
+            mProcsToOomAdj.clear();
+        }
+
         if (proactiveKillsEnabled                               // Proactive kills enabled?
                 && doKillExcessiveProcesses                     // Should kill excessive processes?
                 && freeSwapPercent < lowSwapThresholdPercent    // Swap below threshold?
@@ -1722,6 +1808,10 @@
         int prevProcState = getInitialProcState(app);
         int prevCapability = getInitialCapability(app);
 
+        // Remove any follow up update this process might have. It will be rescheduled if still
+        // needed.
+        app.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME);
+
         if (app.getThread() == null) {
             state.setAdjSeq(mAdjSeq);
             state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND);
@@ -1989,6 +2079,8 @@
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app);
             }
+            maybeSetProcessFollowUpUpdateLocked(app,
+                    state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION, now);
         }
 
         // If the app was recently in the foreground and has expedited jobs running,
@@ -2009,6 +2101,9 @@
             if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                 reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg for EJ: " + app);
             }
+            maybeSetProcessFollowUpUpdateLocked(app,
+                    state.getLastTopTime() + mConstants.TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION,
+                    now);
         }
 
         if (adj > PERCEPTIBLE_APP_ADJ
@@ -2074,7 +2169,7 @@
             // app to be demoted to cached.
             if (procState >= PROCESS_STATE_LAST_ACTIVITY
                     && state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY
-                    && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) < now) {
+                    && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) <= now) {
                 procState = PROCESS_STATE_LAST_ACTIVITY;
                 schedGroup = SCHED_GROUP_BACKGROUND;
                 state.setAdjType("previous-expired");
@@ -2098,6 +2193,14 @@
                         reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app);
                     }
                 }
+                final long lastStateTime;
+                if (state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY) {
+                    lastStateTime = state.getLastStateTime();
+                } else {
+                    lastStateTime = now;
+                }
+                maybeSetProcessFollowUpUpdateLocked(app,
+                        lastStateTime + mConstants.MAX_PREVIOUS_TIME, now);
             }
         }
 
@@ -2186,6 +2289,8 @@
                                 reportOomAdjMessageLocked(TAG_OOM_ADJ,
                                         "Raise adj to started service: " + app);
                             }
+                            maybeSetProcessFollowUpUpdateLocked(app,
+                                    s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY, now);
                         }
                     }
                     // If we have let the service slide into the background
@@ -2205,14 +2310,8 @@
                                     != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
 
                     if (roForegroundAudioControl()) { // flag check
-                        // TODO revisit restriction of FOREGROUND_AUDIO_CONTROL when it can be
-                        //      limited to specific FGS types
-                        //final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
-                        //        | FOREGROUND_SERVICE_TYPE_CAMERA
-                        //        | FOREGROUND_SERVICE_TYPE_MICROPHONE
-                        //        | FOREGROUND_SERVICE_TYPE_PHONE_CALL;
-                        //capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0
-                        //        ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0;
+                        // TODO(b/335373208) - revisit restriction of FOREGROUND_AUDIO_CONTROL
+                        //  when it can be limited to specific FGS types
                         capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
                     }
 
@@ -2340,6 +2439,8 @@
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
                             "Raise adj to recent provider: " + app);
                 }
+                maybeSetProcessFollowUpUpdateLocked(app,
+                        ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now);
             }
             if (procState > PROCESS_STATE_LAST_ACTIVITY) {
                 procState = PROCESS_STATE_LAST_ACTIVITY;
@@ -2348,6 +2449,8 @@
                     reportOomAdjMessageLocked(TAG_OOM_ADJ,
                             "Raise procstate to recent provider: " + app);
                 }
+                maybeSetProcessFollowUpUpdateLocked(app,
+                        ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now);
             }
         }
 
@@ -3246,10 +3349,16 @@
         mCachedAppOptimizer.onWakefulnessChanged(wakefulness);
     }
 
+    @GuardedBy({"mService", "mProcLock"})
+    protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
+            long nowElapsed, @OomAdjReason int oomAdjReason) {
+        return applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, false);
+    }
+
     /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */
     @GuardedBy({"mService", "mProcLock"})
     protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
-            long nowElapsed, @OomAdjReason int oomAdjReson) {
+            long nowElapsed, @OomAdjReason int oomAdjReson, boolean isBatchingOomAdj) {
         boolean success = true;
         final ProcessStateRecord state = app.mState;
         final UidRecord uidRec = app.getUidRecord();
@@ -3266,7 +3375,12 @@
 
         final int oldOomAdj = state.getSetAdj();
         if (state.getCurAdj() != state.getSetAdj()) {
-            ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
+            if (isBatchingOomAdj && mConstants.ENABLE_BATCHING_OOM_ADJ) {
+                mProcsToOomAdj.add(app);
+            } else {
+                ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
+            }
+
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
                 String msg = "Set " + app.getPid() + " " + app.processName + " adj "
                         + state.getCurAdj() + ": " + state.getAdjType();
@@ -3649,7 +3763,7 @@
         if (N <= 0) {
             return;
         }
-        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long nowElapsed = mInjector.getElapsedRealtimeMillis();
         final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME;
         long nextTime = 0;
         if (mService.mLocalPowerManager != null) {
@@ -3884,7 +3998,7 @@
         }
         // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive
         // since it's only evaluating one service connection.
-        return computeServiceHostOomAdjLSP(cr, app, client, SystemClock.uptimeMillis(),
+        return computeServiceHostOomAdjLSP(cr, app, client, mInjector.getUptimeMillis(),
                 mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE,
                 CACHED_APP_MIN_ADJ, false, true /* dryRun */);
     }
@@ -3919,7 +4033,7 @@
             // The provider host process has better states than the client, so no change.
             return false;
         }
-        return computeProviderHostOomAdjLSP(null, app, client, SystemClock.uptimeMillis(),
+        return computeProviderHostOomAdjLSP(null, app, client, mInjector.getUptimeMillis(),
                 mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ,
                 false, true /* dryRun */);
     }
@@ -3947,4 +4061,43 @@
         }
         return false;
     }
+
+    @GuardedBy("mService")
+    private void maybeSetProcessFollowUpUpdateLocked(ProcessRecord proc,
+            long updateUptimeMs, long now) {
+        if (!Flags.followUpOomadjUpdates()) {
+            return;
+        }
+        if (updateUptimeMs <= now) {
+            // Time sensitive period has already passed. No need to schedule a follow up.
+            return;
+        }
+
+        mFollowUpUpdateSet.add(proc);
+        proc.mState.setFollowupUpdateUptimeMs(updateUptimeMs);
+
+        scheduleFollowUpOomAdjusterUpdateLocked(updateUptimeMs, now);
+    }
+
+
+    @GuardedBy("mService")
+    private void scheduleFollowUpOomAdjusterUpdateLocked(long updateUptimeMs,
+            long now) {
+        if (updateUptimeMs + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION
+                >= mNextFollowUpUpdateUptimeMs) {
+            // Update time is too close or later than the next follow up update.
+            return;
+        }
+        if (updateUptimeMs < now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION) {
+            // Use a minimum delay for the follow up to possibly batch multiple process
+            // evaluations and avoid rapid updates.
+            updateUptimeMs = now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION;
+        }
+
+        // Schedulate a follow up update. Don't bother deleting existing handler messages, they
+        // will be cleared during the message while no locks are being held.
+        mNextFollowUpUpdateUptimeMs = updateUptimeMs;
+        mService.mHandler.sendEmptyMessageAtTime(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG,
+                mNextFollowUpUpdateUptimeMs);
+    }
 }
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index a67af89..21842db 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -68,7 +68,6 @@
 import android.app.ActivityManagerInternal.OomAdjReason;
 import android.content.pm.ServiceInfo;
 import android.os.IBinder;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -764,6 +763,11 @@
         super(service, processList, activeUids, adjusterThread);
     }
 
+    OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList,
+            ActiveUids activeUids, Injector injector) {
+        super(service, processList, activeUids, injector);
+    }
+
     private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes(
             ProcessRecordNode.NODE_TYPE_PROC_STATE, PROC_STATE_SLOTS.length);
     private final ProcessRecordNodes mProcessRecordAdjNodes = new ProcessRecordNodes(
@@ -924,8 +928,8 @@
     @GuardedBy({"mService", "mProcLock"})
     private void fullUpdateLSP(@OomAdjReason int oomAdjReason) {
         final ProcessRecord topApp = mService.getTopApp();
-        final long now = SystemClock.uptimeMillis();
-        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long now = mInjector.getUptimeMillis();
+        final long nowElapsed = mInjector.getElapsedRealtimeMillis();
         final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
 
         mAdjSeq++;
@@ -1015,8 +1019,8 @@
     @GuardedBy({"mService", "mProcLock"})
     private void partialUpdateLSP(@OomAdjReason int oomAdjReason, ArraySet<ProcessRecord> targets) {
         final ProcessRecord topApp = mService.getTopApp();
-        final long now = SystemClock.uptimeMillis();
-        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long now = mInjector.getUptimeMillis();
+        final long nowElapsed = mInjector.getElapsedRealtimeMillis();
         final long oldTime = now - mConstants.mMaxEmptyTimeMillis;
 
         ActiveUids activeUids = mTmpUidRecords;
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 76c5952..d4d4965 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -60,6 +60,7 @@
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
 import com.android.server.wm.WindowProcessController;
+import com.android.server.utils.AnrTimer;
 
 import java.io.File;
 import java.io.PrintWriter;
@@ -302,6 +303,9 @@
         SparseBooleanArray lastPids = new SparseBooleanArray(20);
         ActivityManagerService.VolatileDropboxEntryStates volatileDropboxEntriyStates = null;
 
+        // Release the expired timer preparatory to starting the dump or returning without dumping.
+        timeoutRecord.closeExpiredTimer();
+
         if (mApp.isDebugging()) {
             Slog.i(TAG, "Skipping debugged app ANR: " + this + " " + annotation);
             return;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 219de70..c094724 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -354,6 +354,7 @@
     // LMK_KILL_OCCURRED
     // LMK_START_MONITORING
     // LMK_BOOT_COMPLETED
+    // LMK_PROCS_PRIO
     static final byte LMK_TARGET = 0;
     static final byte LMK_PROCPRIO = 1;
     static final byte LMK_PROCREMOVE = 2;
@@ -365,6 +366,7 @@
     static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
     static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
     static final byte LMK_BOOT_COMPLETED = 10;
+    static final byte LMK_PROCS_PRIO = 11;  // Batch option for LMK_PROCPRIO
 
     // Low Memory Killer Daemon command codes.
     // These must be kept in sync with async_event_type definitions in lmkd.h
@@ -1561,6 +1563,50 @@
         }
     }
 
+
+    // The max size for PROCS_PRIO cmd in LMKD
+    private static final int MAX_PROCS_PRIO_PACKET_SIZE = 3;
+
+    // (4 bytes per field * 4 fields * 3 processes per batch) + 4 bytes for the LMKD cmd
+    private static final int MAX_OOM_ADJ_BATCH_LENGTH = ((4 * 4) * MAX_PROCS_PRIO_PACKET_SIZE) + 4;
+
+    /**
+     * Set the out-of-memory badness adjustment for a list of processes.
+     *
+     * @param apps App list to adjust their respective oom score.
+     *
+     * {@hide}
+     */
+    public static void batchSetOomAdj(ArrayList<ProcessRecord> apps) {
+        final int totalApps = apps.size();
+        if (totalApps == 0) {
+            return;
+        }
+
+        ByteBuffer buf = ByteBuffer.allocate(MAX_OOM_ADJ_BATCH_LENGTH);
+        int total_procs_in_buf = 0;
+        buf.putInt(LMK_PROCS_PRIO);
+        for (int i = 0; i < totalApps; i++) {
+            final int pid = apps.get(i).getPid();
+            final int amt = apps.get(i).mState.getCurAdj();
+            final int uid = apps.get(i).uid;
+            if (pid <= 0 || amt == UNKNOWN_ADJ) continue;
+            if (total_procs_in_buf >= MAX_PROCS_PRIO_PACKET_SIZE) {
+                writeLmkd(buf, null);
+                buf.clear();
+                total_procs_in_buf = 0;
+                buf.allocate(MAX_OOM_ADJ_BATCH_LENGTH);
+                buf.putInt(LMK_PROCS_PRIO);
+            }
+            buf.putInt(pid);
+            buf.putInt(uid);
+            buf.putInt(amt);
+            buf.putInt(0);  // Default proc type to PROC_TYPE_APP
+            total_procs_in_buf++;
+        }
+        writeLmkd(buf, null);
+    }
+
     /*
      * {@hide}
      */
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index 8de748e..7c64298 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -449,6 +449,9 @@
     @GuardedBy("mService")
     private boolean mScheduleLikeTopApp = false;
 
+    @GuardedBy("mService")
+    private long mFollowupUpdateUptimeMs = Long.MAX_VALUE;
+
     ProcessStateRecord(ProcessRecord app) {
         mApp = app;
         mService = app.mService;
@@ -1164,6 +1167,16 @@
         mScheduleLikeTopApp = scheduleLikeTopApp;
     }
 
+    @GuardedBy("mService")
+    long getFollowupUpdateUptimeMs() {
+        return mFollowupUpdateUptimeMs;
+    }
+
+    @GuardedBy("mService")
+    void setFollowupUpdateUptimeMs(long updateUptimeMs) {
+        mFollowupUpdateUptimeMs = updateUptimeMs;
+    }
+
     @GuardedBy(anyOf = {"mService", "mProcLock"})
     public String makeAdjReason() {
         if (mAdjSource != null || mAdjTarget != null) {
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 032093b..cbea7aa 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -141,6 +141,8 @@
         "app_widgets",
         "arc_next",
         "art_mainline",
+        "art_performance",
+        "attack_tools",
         "avic",
         "biometrics",
         "biometrics_framework",
@@ -171,6 +173,7 @@
         "haptics",
         "hardware_backed_security_mainline",
         "input",
+        "llvm_and_toolchains",
         "lse_desktop_experience",
         "machine_learning",
         "mainline_modularization",
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c6c1f98..fa0e2ca 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -384,9 +384,11 @@
      * postponed until total number of unlocked users in the system reaches mMaxRunningUsers.
      * Once total number of unlocked users reach mMaxRunningUsers, least recently used user
      * will be locked.
+     *
+     * <p> Note: Even if this is false for the device as a whole, it is possible some users may
+     * individually allow delayed locking, as specified by
+     * {@link UserProperties#getAllowStoppingUserWithDelayedLocking()}.
      */
-    // TODO(b/302662311): Add javadoc changes corresponding to the user property that allows
-    // delayed locking behavior once the private space flag is finalized.
     @GuardedBy("mLock")
     private boolean mDelayUserDataLocking;
 
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index afde4f7..bb52857 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -134,3 +134,21 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "batching_oom_adj"
+    namespace: "backstage_power"
+    description: "Batch OOM adjustment calls to LMKD"
+    bug: "244232958"
+    is_fixed_read_only: true
+}
+
+flag {
+    name: "follow_up_oomadj_updates"
+    namespace: "backstage_power"
+    description: "Schedule follow up OomAdjuster updates for time sensitive states."
+    bug: "333450932"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
index 9f31f37..7417585 100644
--- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -31,7 +31,6 @@
 import android.app.ambientcontext.IAmbientContextObserver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.PackageManagerInternal;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -43,12 +42,10 @@
 
 import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.ambientcontext.AmbientContextManagerPerUserService.ServiceType;
 import com.android.server.infra.AbstractMasterSystemService;
 import com.android.server.infra.FrameworkResourcesServiceNameResolver;
-import com.android.server.pm.KnownPackages;
 
 import com.google.android.collect.Sets;
 
@@ -299,16 +296,6 @@
         return MAX_TEMPORARY_SERVICE_DURATION_MS;
     }
 
-    /** Returns {@code true} if the detection service is configured on this device. */
-    public static boolean isDetectionServiceConfigured() {
-        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-        final String[] packageNames = pmi.getKnownPackageNames(
-                KnownPackages.PACKAGE_AMBIENT_CONTEXT_DETECTION, UserHandle.USER_SYSTEM);
-        boolean isServiceConfigured = (packageNames.length != 0);
-        Slog.i(TAG, "Detection service configured: " + isServiceConfigured);
-        return isServiceConfigured;
-    }
-
     /**
      * Send request to the remote AmbientContextDetectionService impl to start detecting the
      * specified events. Intended for use by shell command for testing.
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 039e7f4..bce1830 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -275,7 +275,7 @@
      * Handle BluetoothHeadset intents where the action is one of
      *   {@link BluetoothHeadset#ACTION_ACTIVE_DEVICE_CHANGED} or
      *   {@link BluetoothHeadset#ACTION_AUDIO_STATE_CHANGED}.
-     * @param intent
+     * @param intent the Intent received from BT service
      */
     private void onReceiveBtEvent(@NonNull Intent intent) {
         mBtHelper.onReceiveBtEvent(intent);
@@ -296,7 +296,7 @@
 
     /**
      * Turns speakerphone on/off
-     * @param on
+     * @param on true to enable speakerphone
      * @param eventSource for logging purposes
      */
     /*package*/ void setSpeakerphoneOn(
@@ -310,21 +310,21 @@
                 on, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged));
     }
 
+    private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;
+
+    /** synchronization for setCommunicationDevice() and getCommunicationDevice */
+    private final Object mCommunicationDeviceLock = new Object();
+    @GuardedBy("mCommunicationDeviceLock")
+    private int mCommunicationDeviceUpdateCount = 0;
+
     /**
      * Select device for use for communication use cases.
      * @param cb Client binder for death detection
      * @param uid Client uid
      * @param device Device selected or null to unselect.
      * @param eventSource for logging purposes
+     * @return false if there is no device and no communication client
      */
-
-    private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;
-
-    /** synchronization for setCommunicationDevice() and getCommunicationDevice */
-    private Object mCommunicationDeviceLock = new Object();
-    @GuardedBy("mCommunicationDeviceLock")
-    private int mCommunicationDeviceUpdateCount = 0;
-
     /*package*/ boolean setCommunicationDevice(IBinder cb, int uid, AudioDeviceInfo device,
                                                boolean isPrivileged, String eventSource) {
 
@@ -355,7 +355,6 @@
      * Sets or resets the communication device for matching client. If no client matches and the
      * request is to reset for a given device (deviceInfo.mOn == false), the method is a noop.
      * @param deviceInfo information on the device and requester {@link #CommunicationDeviceInfo}
-     * @return true if the communication device is set or reset
      */
     @GuardedBy("mDeviceStateLock")
     /*package*/ void onSetCommunicationDeviceForClient(CommunicationDeviceInfo deviceInfo) {
@@ -742,15 +741,6 @@
     }
 
     /**
-     * Helper method on top of isDeviceRequestedForCommunication() indicating if
-     * speakerphone ON is currently requested or not.
-     * @return true if speakerphone ON requested, false otherwise.
-     */
-    private boolean isSpeakerphoneRequested() {
-        return isDeviceRequestedForCommunication(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER);
-    }
-
-    /**
      * Indicates if preferred route selection for communication is speakerphone.
      * @return true if speakerphone is active, false otherwise.
      */
@@ -929,6 +919,12 @@
         }
 
         @Override
+        public int hashCode() {
+            // only hashing on the fields used in equals()
+            return Objects.hash(mProfile, mDevice);
+        }
+
+        @Override
         public String toString() {
             return "BtDeviceInfo: device=" + mDevice.toString()
                             + " state=" + mState
@@ -990,7 +986,7 @@
     /**
      * will block on mDeviceStateLock, which is held during an A2DP (dis) connection
      * not just a simple message post
-     * @param info struct with the (dis)connection information
+     * @param data struct with the (dis)connection information
      */
     /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
         if (data.mPreviousDevice != null
@@ -1379,6 +1375,7 @@
                 mCommDevDispatchers.getBroadcastItem(i)
                         .dispatchCommunicationDeviceChanged(portId);
             } catch (RemoteException e) {
+                Log.e(TAG, "dispatchCommunicationDevice error", e);
             }
         }
         mCommDevDispatchers.finishBroadcast();
@@ -1581,6 +1578,12 @@
         }
 
         @Override
+        public int hashCode() {
+            // only hashing on the fields used in equals()
+            return Objects.hash(mCb.hashCode(), mUid);
+        }
+
+        @Override
         public String toString() {
             return "CommunicationDeviceInfo mCb=" + mCb.toString()
                     + " mUid=" + mUid
@@ -1600,9 +1603,9 @@
     //@GuardedBy("mConnectedDevices")
     /*package*/ void setBluetoothA2dpOnInt(boolean on, boolean fromA2dp, String source) {
         // for logging only
-        final String eventSource = new StringBuilder("setBluetoothA2dpOn(").append(on)
-                .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
-                .append(Binder.getCallingPid()).append(" src:").append(source).toString();
+        final String eventSource = "setBluetoothA2dpOn(" + on
+                + ") from u/pid:" + Binder.getCallingUid() + "/"
+                + Binder.getCallingPid() + " src:" + source;
 
         mBluetoothA2dpEnabled.set(on);
         onSetForceUse(
@@ -2203,10 +2206,6 @@
         sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, delay);
     }
 
-    private void sendIMsg(int msg, int existingMsgPolicy, int arg, int delay) {
-        sendIILMsg(msg, existingMsgPolicy, arg, 0, null, delay);
-    }
-
     private void sendMsgNoDelay(int msg, int existingMsgPolicy) {
         sendIILMsg(msg, existingMsgPolicy, 0, 0, null, 0);
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 287c92f..ba7aee0 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -449,7 +449,7 @@
         @Override
         public DeviceInfo put(String key, DeviceInfo value) {
             final DeviceInfo result = super.put(key, value);
-            record("put", true /* connected */, key, value);
+            record("put", true /* connected */, value);
             return result;
         }
 
@@ -457,7 +457,7 @@
         public DeviceInfo putIfAbsent(String key, DeviceInfo value) {
             final DeviceInfo result = super.putIfAbsent(key, value);
             if (result == null) {
-                record("putIfAbsent", true /* connected */, key, value);
+                record("putIfAbsent", true /* connected */, value);
             }
             return result;
         }
@@ -466,7 +466,7 @@
         public DeviceInfo remove(Object key) {
             final DeviceInfo result = super.remove(key);
             if (result != null) {
-                record("remove", false /* connected */, (String) key, result);
+                record("remove", false /* connected */, result);
             }
             return result;
         }
@@ -475,7 +475,7 @@
         public boolean remove(Object key, Object value) {
             final boolean result = super.remove(key, value);
             if (result) {
-                record("remove", false /* connected */, (String) key, (DeviceInfo) value);
+                record("remove", false /* connected */, (DeviceInfo) value);
             }
             return result;
         }
@@ -489,7 +489,7 @@
         // putAll
         // replace
         // replaceAll
-        private void record(String event, boolean connected, String key, DeviceInfo value) {
+        private void record(String event, boolean connected, DeviceInfo value) {
             // DeviceInfo - int mDeviceType;
             // DeviceInfo - int mDeviceCodecFormat;
             new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
@@ -662,7 +662,7 @@
     /**
      * A class just for packaging up a set of connection parameters.
      */
-    /*package*/ class WiredDeviceConnectionState {
+    /*package*/ static class WiredDeviceConnectionState {
         public final AudioDeviceAttributes mAttributes;
         public final @AudioService.ConnectionState int mState;
         public final String mCaller;
@@ -1054,7 +1054,9 @@
                 IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(n);
                 try {
                     obs.dispatchAudioRoutesChanged(routes);
-                } catch (RemoteException e) { }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "onReportNewRoutes", e);
+                }
             }
         }
         mRoutesObservers.finishBroadcast();
@@ -1835,7 +1837,8 @@
                     .set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
                     .record();
             if (toRemove.size() > 0) {
-                final int delay = checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID,
+                /*final int delay = */
+                checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID,
                         AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE);
                 toRemove.stream().forEach(deviceAddress ->
                         // TODO delay not used?
@@ -2697,10 +2700,6 @@
     private static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
     private static final String CONNECT_INTENT_KEY_STATE = "state";
     private static final String CONNECT_INTENT_KEY_ADDRESS = "address";
-    private static final String CONNECT_INTENT_KEY_HAS_PLAYBACK = "hasPlayback";
-    private static final String CONNECT_INTENT_KEY_HAS_CAPTURE = "hasCapture";
-    private static final String CONNECT_INTENT_KEY_HAS_MIDI = "hasMIDI";
-    private static final String CONNECT_INTENT_KEY_DEVICE_CLASS = "class";
 
     private void sendDeviceConnectionIntent(int device, int state, String address,
                                             String deviceName) {
@@ -2863,6 +2862,7 @@
                 mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
                         strategy, devices);
             } catch (RemoteException e) {
+                Log.e(TAG, "dispatchPreferredDevice ", e);
             }
         }
         mPrefDevDispatchers.finishBroadcast();
@@ -2879,6 +2879,7 @@
                 mNonDefDevDispatchers.getBroadcastItem(i).dispatchNonDefDevicesChanged(
                         strategy, devices);
             } catch (RemoteException e) {
+                Log.e(TAG, "dispatchNonDefaultDevice ", e);
             }
         }
         mNonDefDevDispatchers.finishBroadcast();
@@ -2895,6 +2896,7 @@
                 mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
                         capturePreset, role, devices);
             } catch (RemoteException e) {
+                Log.e(TAG, "dispatchDevicesRoleForCapturePreset ", e);
             }
         }
         mDevRoleCapturePresetDispatchers.finishBroadcast();
@@ -2960,7 +2962,7 @@
 
     /**
      * Check if device is in the list of connected devices
-     * @param device
+     * @param device the device to query
      * @return true if connected
      */
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
index 76191bb..14eae8d 100644
--- a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
+++ b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
@@ -16,10 +16,20 @@
 
 package com.android.server.audio;
 
+import static android.Manifest.permission.ACCESS_ULTRASOUND;
+import static android.Manifest.permission.BLUETOOTH_CONNECT;
 import static android.Manifest.permission.CALL_AUDIO_INTERCEPTION;
+import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.CAPTURE_AUDIO_OUTPUT;
+import static android.Manifest.permission.CAPTURE_MEDIA_OUTPUT;
+import static android.Manifest.permission.CAPTURE_TUNER_AUDIO_INPUT;
+import static android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT;
 import static android.Manifest.permission.MODIFY_AUDIO_ROUTING;
+import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS;
+import static android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS;
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
 import static android.Manifest.permission.RECORD_AUDIO;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 
 import android.annotation.Nullable;
 import android.os.RemoteException;
@@ -52,10 +62,21 @@
     static final String[] MONITORED_PERMS = new String[PermissionEnum.ENUM_SIZE];
 
     static {
-        MONITORED_PERMS[PermissionEnum.MODIFY_AUDIO_ROUTING] = MODIFY_AUDIO_ROUTING;
-        MONITORED_PERMS[PermissionEnum.MODIFY_PHONE_STATE] = MODIFY_PHONE_STATE;
         MONITORED_PERMS[PermissionEnum.RECORD_AUDIO] = RECORD_AUDIO;
+        MONITORED_PERMS[PermissionEnum.MODIFY_AUDIO_ROUTING] = MODIFY_AUDIO_ROUTING;
+        MONITORED_PERMS[PermissionEnum.MODIFY_AUDIO_SETTINGS] = MODIFY_AUDIO_SETTINGS;
+        MONITORED_PERMS[PermissionEnum.MODIFY_PHONE_STATE] = MODIFY_PHONE_STATE;
+        MONITORED_PERMS[PermissionEnum.MODIFY_DEFAULT_AUDIO_EFFECTS] = MODIFY_DEFAULT_AUDIO_EFFECTS;
+        MONITORED_PERMS[PermissionEnum.WRITE_SECURE_SETTINGS] = WRITE_SECURE_SETTINGS;
         MONITORED_PERMS[PermissionEnum.CALL_AUDIO_INTERCEPTION] = CALL_AUDIO_INTERCEPTION;
+        MONITORED_PERMS[PermissionEnum.ACCESS_ULTRASOUND] = ACCESS_ULTRASOUND;
+        MONITORED_PERMS[PermissionEnum.CAPTURE_AUDIO_OUTPUT] = CAPTURE_AUDIO_OUTPUT;
+        MONITORED_PERMS[PermissionEnum.CAPTURE_MEDIA_OUTPUT] = CAPTURE_MEDIA_OUTPUT;
+        MONITORED_PERMS[PermissionEnum.CAPTURE_AUDIO_HOTWORD] = CAPTURE_AUDIO_HOTWORD;
+        MONITORED_PERMS[PermissionEnum.CAPTURE_TUNER_AUDIO_INPUT] = CAPTURE_TUNER_AUDIO_INPUT;
+        MONITORED_PERMS[PermissionEnum.CAPTURE_VOICE_COMMUNICATION_OUTPUT] =
+                CAPTURE_VOICE_COMMUNICATION_OUTPUT;
+        MONITORED_PERMS[PermissionEnum.BLUETOOTH_CONNECT] = BLUETOOTH_CONNECT;
     }
 
     private final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c7ddccc..684cb24 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -10331,7 +10331,7 @@
         try {
             if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid,
                     HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS,
-                    clientId, durationHint, callingPackageName)) {
+                    clientId, durationHint, callingPackageName, attributionTag, sdk)) {
                 final String reason = "Audio focus request blocked by hardening";
                 Log.w(TAG, reason);
                 mmi.set(MediaMetrics.Property.EARLY_RETURN, reason).record();
@@ -10343,7 +10343,7 @@
 
         mmi.record();
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
-                clientId, callingPackageName, attributionTag, flags, sdk,
+                clientId, callingPackageName, flags, sdk,
                 forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/,
                 permissionOverridesCheck);
     }
@@ -10361,7 +10361,7 @@
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
-                clientId, callingPackageName, null, flags,
+                clientId, callingPackageName, flags,
                 sdk, false /*forceDuck*/, fakeUid, true /*permissionOverridesCheck*/);
     }
 
@@ -13911,9 +13911,8 @@
         final int stream = AudioAttributes.toLegacyStreamType(aa);
         final boolean mutingFromVolume = getStreamVolume(stream) == 0;
         if (mutingFromVolume) {
-            if (DEBUG_VOL) {
-                Slog.d(TAG, "notification should not play due to muted stream " + stream);
-            }
+            Slog.i(TAG, "shouldNotificationSoundPlay false: muted stream:" + stream
+                    + " attr:" + aa);
             return false;
         }
 
@@ -13926,10 +13925,8 @@
         // is the owner of GAIN_TRANSIENT_EXCLUSIVE focus also recording?
         final boolean mutingFromFocusAndRecording = mRecordMonitor.isRecordingActiveForUid(uid);
         if (mutingFromFocusAndRecording) {
-            if (DEBUG_VOL) {
-                Slog.d(TAG, "notification should not play due to exclusive focus owner recording "
-                        + " uid:" + uid);
-            }
+            Slog.i(TAG, "shouldNotificationSoundPlay false: exclusive focus owner recording "
+                        + " uid:" + uid + " attr:" + aa);
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
index 409ed17..8ae04ac 100644
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -19,6 +19,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -26,6 +27,7 @@
 import android.media.AudioFocusRequest;
 import android.media.AudioManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -128,19 +130,28 @@
      * @param focusMethod name of the method to check, for logging purposes
      * @param clientId id of the requester
      * @param durationHint focus type being requested
+     * @param attributionTag attribution of the caller
+     * @param targetSdk target SDK of the caller
      * @return false if the method call is allowed, true if it should be a no-op
      */
+    @SuppressWarnings("AndroidFrameworkCompatChange")
     protected boolean blockFocusMethod(int callingUid, int focusMethod, @NonNull String clientId,
-            int durationHint, @NonNull String packageName) {
+            int durationHint, @NonNull String packageName, String attributionTag, int targetSdk) {
         if (packageName.isEmpty()) {
             packageName = getPackNameForUid(callingUid);
         }
 
-        if (checkAppOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName)) {
+        if (noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName, attributionTag)) {
             if (DEBUG) {
                 Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking");
             }
             return false;
+        } else if (targetSdk < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            if (DEBUG) {
+                Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking due to sdk="
+                        + targetSdk);
+            }
+            return false;
         }
 
         String errorMssg = "Focus request DENIED for uid:" + callingUid
@@ -169,14 +180,17 @@
     }
 
     /**
-     * Checks the given op without throwing
+     * Notes the given op without throwing
      * @param op the appOp code
      * @param uid the calling uid
      * @param packageName the package name of the caller
+     * @param attributionTag attribution of the caller
      * @return return false if the operation is not allowed
      */
-    private boolean checkAppOp(int op, int uid, @NonNull String packageName) {
-        if (mAppOps.checkOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+    private boolean noteOp(int op, int uid, @NonNull String packageName,
+            @Nullable String attributionTag) {
+        if (mAppOps.noteOpNoThrow(op, uid, packageName, attributionTag, null)
+                != AppOpsManager.MODE_ALLOWED) {
             return false;
         }
         return true;
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 35d38e2..70f3193 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -1082,7 +1082,6 @@
      * @param fd
      * @param clientId
      * @param callingPackageName
-     * @param attributionTag
      * @param flags
      * @param sdk
      * @param forceDuck only true if
@@ -1096,7 +1095,7 @@
      */
     protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
-            String attributionTag, int flags, int sdk, boolean forceDuck, int testUid,
+            int flags, int sdk, boolean forceDuck, int testUid,
             boolean permissionOverridesCheck) {
         new MediaMetrics.Item(mMetricsId)
                 .setUid(Binder.getCallingUid())
@@ -1129,12 +1128,6 @@
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
 
-        final int res = mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
-                callingPackageName, attributionTag, null);
-        if (!permissionOverridesCheck && res != AppOpsManager.MODE_ALLOWED) {
-            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
-        }
-
         synchronized(mAudioFocusLock) {
             // check whether a focus freeze is in place and filter
             if (isFocusFrozenForTest()) {
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 469e8b7..276ab03 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -108,6 +108,7 @@
     }
 
     private final Context mContext;
+    private final BiometricManager mBiometricManager;
     @NonNull private final BiometricContext mBiometricContext;
     private final IStatusBarService mStatusBarService;
     @VisibleForTesting final IBiometricSysuiReceiver mSysuiReceiver;
@@ -131,6 +132,7 @@
     private final String mOpPackageName;
     private final boolean mDebugEnabled;
     private final List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
+    private final List<Integer> mSfpsSensorIds;
 
     // The current state, which can be either idle, called, or started
     private @SessionState int mState = STATE_AUTH_IDLE;
@@ -220,6 +222,11 @@
         mCancelled = false;
         mBiometricFrameworkStatsLogger = logger;
         mOperationContext = new OperationContextExt(true /* isBP */);
+        mBiometricManager = mContext.getSystemService(BiometricManager.class);
+
+        mSfpsSensorIds = mFingerprintSensorProperties.stream().filter(
+                FingerprintSensorPropertiesInternal::isAnySidefpsType).map(
+                    prop -> prop.sensorId).toList();
 
         try {
             mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
@@ -316,7 +323,7 @@
             Slog.w(TAG, "Received cookie but already cancelled (ignoring): " + cookie);
             return;
         }
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onCookieReceived after successful auth");
             return;
         }
@@ -494,6 +501,7 @@
             }
 
             case STATE_AUTH_STARTED:
+            case STATE_AUTH_PENDING_CONFIRM:
             case STATE_AUTH_STARTED_UI_SHOWING: {
                 if (isAllowDeviceCredential() && errorLockout) {
                     // SystemUI handles transition from biometric to device credential.
@@ -539,7 +547,7 @@
     }
 
     void onAcquired(int sensorId, int acquiredInfo, int vendorCode) {
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onAcquired after successful auth");
             return;
         }
@@ -562,7 +570,7 @@
     }
 
     void onSystemEvent(int event) {
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onSystemEvent after successful auth");
             return;
         }
@@ -579,12 +587,15 @@
 
     void onDialogAnimatedIn(boolean startFingerprintNow) {
         if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI
-                && mState != STATE_AUTH_PAUSED) {
+                && mState != STATE_AUTH_PAUSED && mState != STATE_AUTH_PENDING_CONFIRM) {
             Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState);
             return;
         }
 
-        mState = STATE_AUTH_STARTED_UI_SHOWING;
+        if (mState != STATE_AUTH_PENDING_CONFIRM) {
+            mState = STATE_AUTH_STARTED_UI_SHOWING;
+        }
+
         if (startFingerprintNow) {
             startAllPreparedFingerprintSensors();
         } else {
@@ -600,6 +611,7 @@
         if (mState != STATE_AUTH_STARTED
                 && mState != STATE_AUTH_STARTED_UI_SHOWING
                 && mState != STATE_AUTH_PAUSED
+                && mState != STATE_AUTH_PENDING_CONFIRM
                 && mState != STATE_ERROR_PENDING_SYSUI) {
             Slog.w(TAG, "onStartFingerprint, started from unexpected state: " + mState);
         }
@@ -608,7 +620,7 @@
     }
 
     void onTryAgainPressed() {
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onTryAgainPressed after successful auth");
             return;
         }
@@ -625,7 +637,7 @@
     }
 
     void onAuthenticationSucceeded(int sensorId, boolean strong, byte[] token) {
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onAuthenticationSucceeded after successful auth");
             return;
         }
@@ -656,11 +668,17 @@
             Slog.e(TAG, "RemoteException", e);
         }
 
-        cancelAllSensors(sensor -> sensor.id != sensorId);
+        if (mState == STATE_AUTH_PENDING_CONFIRM) {
+            // Do not cancel Sfps sensors so auth can continue running
+            cancelAllSensors(
+                    sensor -> sensor.id != sensorId && !mSfpsSensorIds.contains(sensor.id));
+        } else {
+            cancelAllSensors(sensor -> sensor.id != sensorId);
+        }
     }
 
     void onAuthenticationRejected(int sensorId) {
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onAuthenticationRejected after successful auth");
             return;
         }
@@ -678,7 +696,7 @@
     }
 
     void onAuthenticationTimedOut(int sensorId, int cookie, int error, int vendorCode) {
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onAuthenticationTimedOut after successful auth");
             return;
         }
@@ -703,7 +721,7 @@
     }
 
     void onDeviceCredentialPressed() {
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onDeviceCredentialPressed after successful auth");
             return;
         }
@@ -739,6 +757,10 @@
         return mAuthenticatedSensorId != -1;
     }
 
+    private boolean hasAuthenticatedAndConfirmed() {
+        return mAuthenticatedSensorId != -1 && mState == STATE_AUTHENTICATED_PENDING_SYSUI;
+    }
+
     private void logOnDialogDismissed(@BiometricPrompt.DismissedReason int reason) {
         if (reason == BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED) {
             // Explicit auth, authentication confirmed.
@@ -828,6 +850,7 @@
                     } else {
                         Slog.e(TAG, "mTokenEscrow is null");
                     }
+
                     mClientReceiver.onAuthenticationSucceeded(
                             Utils.getAuthenticationTypeForResult(reason));
                     break;
@@ -861,6 +884,16 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
         } finally {
+            if (mTokenEscrow != null && mBiometricManager != null) {
+                final byte[] byteToken = new byte[mTokenEscrow.length];
+                for (int i = 0; i < mTokenEscrow.length; i++) {
+                    byteToken[i] = mTokenEscrow[i];
+                }
+                mBiometricManager.resetLockoutTimeBound(mToken,
+                        mContext.getOpPackageName(),
+                        mAuthenticatedSensorId, mUserId, byteToken);
+            }
+
             // ensure everything is cleaned up when dismissed
             cancelAllSensors();
         }
@@ -874,7 +907,7 @@
      * @return true if this AuthSession is finished, e.g. should be set to null
      */
     boolean onCancelAuthSession(boolean force) {
-        if (hasAuthenticated()) {
+        if (hasAuthenticatedAndConfirmed()) {
             Slog.d(TAG, "onCancelAuthSession after successful auth");
             return true;
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 1e2451c..daaafcb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -242,14 +242,14 @@
                 byteToken[i] = hardwareAuthToken.get(i);
             }
 
-            if (mIsStrongBiometric) {
-                mBiometricManager.resetLockoutTimeBound(getToken(),
-                        getContext().getOpPackageName(),
-                        getSensorId(), getTargetUserId(), byteToken);
-            }
-
             // For BP, BiometricService will add the authToken to Keystore.
-            if (!isBiometricPrompt() && mIsStrongBiometric) {
+            if (!isBiometricPrompt()) {
+                if (mIsStrongBiometric) {
+                    mBiometricManager.resetLockoutTimeBound(getToken(),
+                            getContext().getOpPackageName(),
+                            getSensorId(), getTargetUserId(), byteToken);
+                }
+
                 final int result = KeyStoreAuthorization.getInstance().addAuthToken(byteToken);
                 if (result != 0) {
                     Slog.d(TAG, "Error adding auth token : " + result);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index f9f56ee..72d92b9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -209,7 +209,7 @@
             Slog.d(TAG, "Lockout is implemented by the HAL");
             return;
         }
-        if (authenticated) {
+        if (authenticated && !isBiometricPrompt()) {
             getLockoutTracker().resetFailedAttemptsForUser(true /* clearAttemptCounter */,
                     getTargetUserId());
         } else {
@@ -316,6 +316,8 @@
 
                 if (getBiometricContext().isAwake()) {
                     mALSProbeCallback.getProbe().enable();
+                } else {
+                    mALSProbeCallback.getProbe().disable();
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote exception", e);
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
index e6de14b..16514fa 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
@@ -29,6 +29,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -121,8 +122,7 @@
                     + " without permission " + Manifest.permission.DUMP);
             return;
         }
-        android.util.IndentingPrintWriter radioPrintWriter =
-                new android.util.IndentingPrintWriter(printWriter);
+        IndentingPrintWriter radioPrintWriter = new IndentingPrintWriter(printWriter);
         radioPrintWriter.printf("BroadcastRadioService\n");
 
         radioPrintWriter.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index 93fb7b2..ab08342 100644
--- a/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -26,6 +26,7 @@
 import android.hardware.radio.RadioManager;
 import android.os.Binder;
 import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
 
@@ -138,7 +139,7 @@
                     + " without permission " + Manifest.permission.DUMP);
             return;
         }
-        android.util.IndentingPrintWriter radioPw = new android.util.IndentingPrintWriter(pw);
+        IndentingPrintWriter radioPw = new IndentingPrintWriter(pw);
         radioPw.printf("BroadcastRadioService\n");
 
         radioPw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
index 2c8f499..b71589c 100644
--- a/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
+++ b/services/core/java/com/android/server/broadcastradio/RadioEventLogger.java
@@ -17,6 +17,7 @@
 package com.android.server.broadcastradio;
 
 import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
 import android.util.LocalLog;
 import android.util.Log;
 
@@ -54,7 +55,7 @@
      * Dump broadcast radio service event
      * @param pw Indenting print writer for dump
      */
-    public void dump(android.util.IndentingPrintWriter pw) {
+    public void dump(IndentingPrintWriter pw) {
         mEventLogger.dump(pw);
     }
 }
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
index 9654a93..b618aa3 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
@@ -22,6 +22,7 @@
 import android.hardware.radio.ICloseHandle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -93,7 +94,7 @@
             if (mCloseHandle != null) mCloseHandle.close();
         }
 
-        public void dumpInfo(android.util.IndentingPrintWriter pw) {
+        public void dumpInfo(IndentingPrintWriter pw) {
             pw.printf("ModuleWatcher:\n");
 
             pw.increaseIndent();
@@ -191,8 +192,7 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
-        android.util.IndentingPrintWriter announcementPrintWriter =
-                new android.util.IndentingPrintWriter(printWriter);
+        IndentingPrintWriter announcementPrintWriter = new IndentingPrintWriter(printWriter);
         announcementPrintWriter.printf("AnnouncementAggregator\n");
 
         announcementPrintWriter.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index 1c42161..d9f8588 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -128,7 +129,7 @@
                     if (entry.getValue() == mModuleId) {
                         Slogf.w(TAG, "Service %s died, removed RadioModule with ID %d",
                                 entry.getKey(), mModuleId);
-                        return;
+                        break;
                     }
                 }
             }
@@ -260,7 +261,7 @@
      *
      * @param pw The file to which {@link BroadcastRadioServiceImpl} state is dumped.
      */
-    public void dumpInfo(android.util.IndentingPrintWriter pw) {
+    public void dumpInfo(IndentingPrintWriter pw) {
         synchronized (mLock) {
             pw.printf("Next module id available: %d\n", mNextModuleId);
             pw.printf("ServiceName to module id map:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index 5b77c52..9467d6f 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -446,6 +446,7 @@
                         sel.secondaryIds[i]);
                 if (id == null) {
                     Slogf.e(TAG, "invalid secondary id: %s", sel.secondaryIds[i]);
+                    continue;
                 }
                 secondaryIdList.add(id);
             }
@@ -689,9 +690,6 @@
                 || !identifierMeetsSdkVersionRequirement(info.getPhysicallyTunedTo(), uid)) {
             return false;
         }
-        if (info.getRelatedContent() == null) {
-            return true;
-        }
         Iterator<ProgramSelector.Identifier> relatedContentIt = info.getRelatedContent().iterator();
         while (relatedContentIt.hasNext()) {
             if (!identifierMeetsSdkVersionRequirement(relatedContentIt.next(), uid)) {
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 0cac356..03e347a 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -524,7 +525,7 @@
         return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
     }
 
-    void dumpInfo(android.util.IndentingPrintWriter pw) {
+    void dumpInfo(IndentingPrintWriter pw) {
         pw.printf("RadioModule\n");
 
         pw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
index 925f149..e90a1dd 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.broadcastradio.RadioEventLogger;
@@ -434,7 +435,7 @@
         }
     }
 
-    void dumpInfo(android.util.IndentingPrintWriter pw) {
+    void dumpInfo(IndentingPrintWriter pw) {
         pw.printf("TunerSession\n");
 
         pw.increaseIndent();
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index e1650c2..a4efa2e 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -30,6 +30,7 @@
 import android.os.IHwBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -115,7 +116,7 @@
                     if (entry.getValue() == moduleId) {
                         Slogf.i(TAG, "service " + entry.getKey()
                                 + " died; removed RadioModule with ID " + moduleId);
-                        return;
+                        break;
                     }
                 }
             }
@@ -221,7 +222,7 @@
      *
      * @param pw The file to which BroadcastRadioService state is dumped.
      */
-    public void dumpInfo(android.util.IndentingPrintWriter pw) {
+    public void dumpInfo(IndentingPrintWriter pw) {
         synchronized (mLock) {
             pw.printf("Next module id available: %d\n", mNextModuleId);
             pw.printf("ServiceName to module id map:\n");
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 34bfa6c..02a9f09 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -304,10 +304,7 @@
 
     private static boolean isEmpty(
             @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) {
-        if (sel.primaryId.type != 0) return false;
-        if (sel.primaryId.value != 0) return false;
-        if (!sel.secondaryIds.isEmpty()) return false;
-        return true;
+        return sel.primaryId.type == 0 && sel.primaryId.value == 0 && sel.secondaryIds.isEmpty();
     }
 
     static @Nullable ProgramSelector programSelectorFromHal(
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 7269f24..d3b2448 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -40,6 +40,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 import android.util.MutableInt;
 
 import com.android.internal.annotations.GuardedBy;
@@ -453,7 +454,7 @@
         return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
     }
 
-    void dumpInfo(android.util.IndentingPrintWriter pw) {
+    void dumpInfo(IndentingPrintWriter pw) {
         pw.printf("RadioModule\n");
         pw.increaseIndent();
         pw.printf("BroadcastRadioService: %s\n", mService);
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index b1b5d34..80efacd 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -31,6 +31,7 @@
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
 import android.util.MutableBoolean;
 import android.util.MutableInt;
 
@@ -324,9 +325,7 @@
         try {
             isConfigFlagSet(flag);
             return true;
-        } catch (IllegalStateException ex) {
-            return true;
-        } catch (UnsupportedOperationException ex) {
+        } catch (IllegalStateException | UnsupportedOperationException ex) {
             return false;
         }
     }
@@ -389,7 +388,7 @@
         }
     }
 
-    void dumpInfo(android.util.IndentingPrintWriter pw) {
+    void dumpInfo(IndentingPrintWriter pw) {
         pw.printf("TunerSession\n");
         pw.increaseIndent();
         pw.printf("HIDL HAL Session: %s\n", mHwSession);
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 390ee96..17835b2 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -18,6 +18,10 @@
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.os.Build.VERSION_CODES.M;
 
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT;
+import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -40,6 +44,7 @@
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.hardware.CameraExtensionSessionStats;
+import android.hardware.CameraFeatureCombinationStats;
 import android.hardware.CameraSessionStats;
 import android.hardware.CameraStreamStats;
 import android.hardware.ICameraService;
@@ -217,7 +222,7 @@
 
     // Map of currently active camera IDs
     private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
-    private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
+    private final List<CameraEvent> mCameraEventHistory = new ArrayList<CameraEvent>();
 
     private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
     private static final IBinder nfcInterfaceToken = new Binder();
@@ -228,9 +233,16 @@
             /*corePoolSize*/ 1);
 
     /**
+     * Interface to track camera analytics
+     */
+    private interface CameraEvent {
+        void logSelf();
+    }
+
+    /**
      * Structure to track camera usage
      */
-    private static class CameraUsageEvent {
+    private static class CameraUsageEvent implements CameraEvent {
         public final String mCameraId;
         public final int mCameraFacing;
         public final String mClientName;
@@ -311,6 +323,214 @@
         public long getDuration() {
             return mCompleted ? mDurationOrStartTimeMs : 0;
         }
+
+        public void logSelf() {
+            int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
+            switch(mCameraFacing) {
+                case CameraSessionStats.CAMERA_FACING_BACK:
+                    facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
+                    break;
+                case CameraSessionStats.CAMERA_FACING_FRONT:
+                    facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
+                    break;
+                case CameraSessionStats.CAMERA_FACING_EXTERNAL:
+                    facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
+                    break;
+                default:
+                    Slog.w(TAG, "Unknown camera facing: " + mCameraFacing);
+            }
+
+            int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE;
+            boolean extensionIsAdvanced = false;
+            int extensionCaptureFormat = ImageFormat.UNKNOWN;
+            if (mExtSessionStats != null) {
+                switch (mExtSessionStats.type) {
+                    case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC:
+                        extensionType = FrameworkStatsLog
+                                .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC;
+                        break;
+                    case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH:
+                        extensionType = FrameworkStatsLog
+                                .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH;
+                        break;
+                    case CameraExtensionSessionStats.Type.EXTENSION_BOKEH:
+                        extensionType =
+                                FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH;
+                        break;
+                    case CameraExtensionSessionStats.Type.EXTENSION_HDR:
+                        extensionType =
+                                FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR;
+                        break;
+                    case CameraExtensionSessionStats.Type.EXTENSION_NIGHT:
+                        extensionType =
+                                FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT;
+                        break;
+                    default:
+                        Slog.w(TAG, "Unknown extension type: " + mExtSessionStats.type);
+                }
+                extensionIsAdvanced = mExtSessionStats.isAdvanced;
+                if (Flags.analytics24q3()) {
+                    extensionCaptureFormat = mExtSessionStats.captureFormat;
+                }
+            }
+
+            int streamCount = 0;
+            if (mStreamStats != null) {
+                streamCount = mStreamStats.size();
+            }
+            if (CameraServiceProxy.DEBUG) {
+                String ultrawideDebug = Flags.logUltrawideUsage()
+                        ? ", wideAngleUsage " + mUsedUltraWide
+                        : "";
+                String zoomOverrideDebug = Flags.logZoomOverrideUsage()
+                        ? ", zoomOverrideUsage " + mUsedZoomOverride
+                        : "";
+                String mostRequestedFpsRangeDebug = Flags.analytics24q3()
+                        ? ", mostRequestedFpsRange " + mMostRequestedFpsRange
+                        : "";
+                String extensionCaptureFormatDebug = Flags.analytics24q3()
+                        ? " extensionCaptureFormat " + mExtSessionStats.captureFormat
+                        : "";
+
+                Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + mAction
+                        + " clientName " + mClientName
+                        + ", duration " + getDuration()
+                        + ", APILevel " + mAPILevel
+                        + ", cameraId " + mCameraId
+                        + ", facing " + facing
+                        + ", isNdk " + mIsNdk
+                        + ", latencyMs " + mLatencyMs
+                        + ", operatingMode " + mOperatingMode
+                        + ", internalReconfigure " + mInternalReconfigure
+                        + ", requestCount " + mRequestCount
+                        + ", resultErrorCount " + mResultErrorCount
+                        + ", deviceError " + mDeviceError
+                        + ", streamCount is " + streamCount
+                        + ", userTag is " + mUserTag
+                        + ", videoStabilizationMode " + mVideoStabilizationMode
+                        + ultrawideDebug
+                        + zoomOverrideDebug
+                        + mostRequestedFpsRangeDebug
+                        + ", logId " + mLogId
+                        + ", sessionIndex " + mSessionIndex
+                        + ", mExtSessionStats {type " + extensionType
+                        + " isAdvanced " + extensionIsAdvanced
+                        + extensionCaptureFormatDebug + "}");
+            }
+
+            // Convert from CameraStreamStats to CameraStreamProto
+            CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS];
+            for (int i = 0; i < MAX_STREAM_STATISTICS; i++) {
+                streamProtos[i] = new CameraStreamProto();
+                if (i < streamCount) {
+                    CameraStreamStats streamStats = mStreamStats.get(i);
+                    streamProtos[i].width = streamStats.getWidth();
+                    streamProtos[i].height = streamStats.getHeight();
+                    streamProtos[i].format = streamStats.getFormat();
+                    streamProtos[i].dataSpace = streamStats.getDataSpace();
+                    streamProtos[i].usage = streamStats.getUsage();
+                    streamProtos[i].requestCount = streamStats.getRequestCount();
+                    streamProtos[i].errorCount = streamStats.getErrorCount();
+                    streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs();
+                    streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers();
+                    streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers();
+                    streamProtos[i].histogramType = streamStats.getHistogramType();
+                    streamProtos[i].histogramBins = streamStats.getHistogramBins();
+                    streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
+                    streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile();
+                    streamProtos[i].streamUseCase = streamStats.getStreamUseCase();
+                    streamProtos[i].colorSpace = streamStats.getColorSpace();
+
+                    if (CameraServiceProxy.DEBUG) {
+                        String histogramTypeName =
+                                cameraHistogramTypeToString(streamProtos[i].histogramType);
+                        Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width
+                                + ", height " + streamProtos[i].height
+                                + ", format " + streamProtos[i].format
+                                + ", maxPreviewFps " + streamStats.getMaxPreviewFps()
+                                + ", dataSpace " + streamProtos[i].dataSpace
+                                + ", usage " + streamProtos[i].usage
+                                + ", requestCount " + streamProtos[i].requestCount
+                                + ", errorCount " + streamProtos[i].errorCount
+                                + ", firstCaptureLatencyMillis "
+                                + streamProtos[i].firstCaptureLatencyMillis
+                                + ", maxHalBuffers " + streamProtos[i].maxHalBuffers
+                                + ", maxAppBuffers " + streamProtos[i].maxAppBuffers
+                                + ", histogramType " + histogramTypeName
+                                + ", histogramBins "
+                                + Arrays.toString(streamProtos[i].histogramBins)
+                                + ", histogramCounts "
+                                + Arrays.toString(streamProtos[i].histogramCounts)
+                                + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile
+                                + ", streamUseCase " + streamProtos[i].streamUseCase
+                                + ", colorSpace " + streamProtos[i].colorSpace);
+                    }
+                }
+            }
+            FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, getDuration(),
+                    mAPILevel, mClientName, facing, mCameraId, mAction, mIsNdk,
+                    mLatencyMs, mOperatingMode, mInternalReconfigure,
+                    mRequestCount, mResultErrorCount, mDeviceError,
+                    streamCount, MessageNano.toByteArray(streamProtos[0]),
+                    MessageNano.toByteArray(streamProtos[1]),
+                    MessageNano.toByteArray(streamProtos[2]),
+                    MessageNano.toByteArray(streamProtos[3]),
+                    MessageNano.toByteArray(streamProtos[4]),
+                    mUserTag, mVideoStabilizationMode,
+                    mLogId, mSessionIndex,
+                    extensionType, extensionIsAdvanced, mUsedUltraWide,
+                    mUsedZoomOverride,
+                    mMostRequestedFpsRange.getLower(), mMostRequestedFpsRange.getUpper(),
+                    extensionCaptureFormat);
+
+        }
+    }
+    /**
+     * Structure to track feature combination query
+     */
+    private static class CameraFeatureCombinationQueryEvent implements CameraEvent {
+        private CameraFeatureCombinationStats mFeatureCombinationStats;
+
+        CameraFeatureCombinationQueryEvent(CameraFeatureCombinationStats featureCombinationStats) {
+            mFeatureCombinationStats = featureCombinationStats;
+        }
+
+        public void logSelf() {
+            int statusCode = -1;
+            switch (mFeatureCombinationStats.mStatus) {
+                case 0:
+                    statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK;
+                    break;
+                case ICameraService.ERROR_ILLEGAL_ARGUMENT:
+                    statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT;
+                    break;
+                case ICameraService.ERROR_INVALID_OPERATION:
+                    statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION;
+                    break;
+                default:
+                    break;
+            }
+            if (statusCode == -1) {
+                Slog.w(TAG, "Unknown feature combination query status code: "
+                        + mFeatureCombinationStats.mStatus);
+                return;
+            }
+
+            if (CameraServiceProxy.DEBUG) {
+                Slog.v(TAG, "CAMERA_FEATURE_COMBINATION_QUERY_EVENT: uid "
+                        + mFeatureCombinationStats.mUid
+                        + ", cameraId " + mFeatureCombinationStats.mCameraId
+                        + ", queryType " + mFeatureCombinationStats.mQueryType
+                        + ", featureCombination " + mFeatureCombinationStats.mFeatureCombination
+                        + ", status " + statusCode);
+            }
+            FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT,
+                    mFeatureCombinationStats.mUid,
+                    mFeatureCombinationStats.mCameraId,
+                    mFeatureCombinationStats.mQueryType,
+                    mFeatureCombinationStats.mFeatureCombination,
+                    statusCode);
+        }
     }
 
     private final class DisplayWindowListener extends IDisplayWindowListener.Stub {
@@ -625,6 +845,32 @@
         }
 
         @Override
+        public void notifyFeatureCombinationStats(CameraFeatureCombinationStats featureCombStats) {
+            if (!Flags.analytics24q3()) {
+                return;
+            }
+            if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
+                Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected "
+                        + " camera service UID!");
+                return;
+            }
+
+            if (DEBUG) {
+                String combinationTypeStr = cameraFeatureCombinationTypeToString(
+                        featureCombStats.mQueryType);
+                String combinationStr = cameraFeatureCombinationToString(
+                        featureCombStats.mFeatureCombination);
+                Slog.v(TAG, "Camera " + featureCombStats.mCameraId
+                        + " query " + combinationTypeStr
+                        + " combination " + combinationStr
+                        + " for client UID " + featureCombStats.mUid
+                        + " returns " + featureCombStats.mStatus);
+            }
+
+            updateFeatureCombinationQuery(featureCombStats);
+        }
+
+        @Override
         public boolean isCameraDisabled(int userId) {
             if (Binder.getCallingUid() != Process.CAMERASERVER_UID) {
                 Slog.e(TAG, "Calling UID: " + Binder.getCallingUid()
@@ -684,7 +930,7 @@
                     switch (cmd.replace('-', '_')) {
                         case "dump_events":
                             int eventCount = mCameraServiceProxy.getUsageEventCount();
-                            mCameraServiceProxy.dumpUsageEvents();
+                            mCameraServiceProxy.dumpCameraEvents();
                             pw.println("Camera usage events dumped: " + eventCount);
                             break;
                         default:
@@ -865,18 +1111,18 @@
     }
 
     private class EventWriterTask implements Runnable {
-        private ArrayList<CameraUsageEvent> mEventList;
+        private List<CameraEvent> mEventList;
         private static final long WRITER_SLEEP_MS = 100;
 
-        public EventWriterTask(ArrayList<CameraUsageEvent> eventList) {
+        EventWriterTask(List<CameraEvent> eventList) {
             mEventList = eventList;
         }
 
         @Override
         public void run() {
             if (mEventList != null) {
-                for (CameraUsageEvent event : mEventList) {
-                    logCameraUsageEvent(event);
+                for (CameraEvent event : mEventList) {
+                    event.logSelf();
                     try {
                         Thread.sleep(WRITER_SLEEP_MS);
                     } catch (InterruptedException e) {}
@@ -884,170 +1130,6 @@
                 mEventList.clear();
             }
         }
-
-        /**
-         * Write camera usage events to stats log.
-         * Package-private
-         */
-        private void logCameraUsageEvent(CameraUsageEvent e) {
-            int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
-            switch(e.mCameraFacing) {
-                case CameraSessionStats.CAMERA_FACING_BACK:
-                    facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
-                    break;
-                case CameraSessionStats.CAMERA_FACING_FRONT:
-                    facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
-                    break;
-                case CameraSessionStats.CAMERA_FACING_EXTERNAL:
-                    facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
-                    break;
-                default:
-                    Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing);
-            }
-
-            int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE;
-            boolean extensionIsAdvanced = false;
-            int extensionCaptureFormat = ImageFormat.UNKNOWN;
-            if (e.mExtSessionStats != null) {
-                switch (e.mExtSessionStats.type) {
-                    case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC:
-                        extensionType = FrameworkStatsLog
-                                .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC;
-                        break;
-                    case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH:
-                        extensionType = FrameworkStatsLog
-                                .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH;
-                        break;
-                    case CameraExtensionSessionStats.Type.EXTENSION_BOKEH:
-                        extensionType =
-                                FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH;
-                        break;
-                    case CameraExtensionSessionStats.Type.EXTENSION_HDR:
-                        extensionType =
-                                FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR;
-                        break;
-                    case CameraExtensionSessionStats.Type.EXTENSION_NIGHT:
-                        extensionType =
-                                FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT;
-                        break;
-                    default:
-                        Slog.w(TAG, "Unknown extension type: " + e.mExtSessionStats.type);
-                }
-                extensionIsAdvanced = e.mExtSessionStats.isAdvanced;
-                if (Flags.analytics24q3()) {
-                    extensionCaptureFormat = e.mExtSessionStats.captureFormat;
-                }
-            }
-
-            int streamCount = 0;
-            if (e.mStreamStats != null) {
-                streamCount = e.mStreamStats.size();
-            }
-            if (CameraServiceProxy.DEBUG) {
-                String ultrawideDebug = Flags.logUltrawideUsage()
-                        ? ", wideAngleUsage " + e.mUsedUltraWide
-                        : "";
-                String zoomOverrideDebug = Flags.logZoomOverrideUsage()
-                        ? ", zoomOverrideUsage " + e.mUsedZoomOverride
-                        : "";
-                String mostRequestedFpsRangeDebug = Flags.analytics24q3()
-                        ? ", mostRequestedFpsRange " + e.mMostRequestedFpsRange
-                        : "";
-                String extensionCaptureFormatDebug = Flags.analytics24q3()
-                        ? " extensionCaptureFormat " + e.mExtSessionStats.captureFormat
-                        : "";
-
-                Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction
-                        + " clientName " + e.mClientName
-                        + ", duration " + e.getDuration()
-                        + ", APILevel " + e.mAPILevel
-                        + ", cameraId " + e.mCameraId
-                        + ", facing " + facing
-                        + ", isNdk " + e.mIsNdk
-                        + ", latencyMs " + e.mLatencyMs
-                        + ", operatingMode " + e.mOperatingMode
-                        + ", internalReconfigure " + e.mInternalReconfigure
-                        + ", requestCount " + e.mRequestCount
-                        + ", resultErrorCount " + e.mResultErrorCount
-                        + ", deviceError " + e.mDeviceError
-                        + ", streamCount is " + streamCount
-                        + ", userTag is " + e.mUserTag
-                        + ", videoStabilizationMode " + e.mVideoStabilizationMode
-                        + ultrawideDebug
-                        + zoomOverrideDebug
-                        + mostRequestedFpsRangeDebug
-                        + ", logId " + e.mLogId
-                        + ", sessionIndex " + e.mSessionIndex
-                        + ", mExtSessionStats {type " + extensionType
-                        + " isAdvanced " + extensionIsAdvanced
-                        + extensionCaptureFormatDebug + "}");
-            }
-
-            // Convert from CameraStreamStats to CameraStreamProto
-            CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS];
-            for (int i = 0; i < MAX_STREAM_STATISTICS; i++) {
-                streamProtos[i] = new CameraStreamProto();
-                if (i < streamCount) {
-                    CameraStreamStats streamStats = e.mStreamStats.get(i);
-                    streamProtos[i].width = streamStats.getWidth();
-                    streamProtos[i].height = streamStats.getHeight();
-                    streamProtos[i].format = streamStats.getFormat();
-                    streamProtos[i].dataSpace = streamStats.getDataSpace();
-                    streamProtos[i].usage = streamStats.getUsage();
-                    streamProtos[i].requestCount = streamStats.getRequestCount();
-                    streamProtos[i].errorCount = streamStats.getErrorCount();
-                    streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs();
-                    streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers();
-                    streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers();
-                    streamProtos[i].histogramType = streamStats.getHistogramType();
-                    streamProtos[i].histogramBins = streamStats.getHistogramBins();
-                    streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
-                    streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile();
-                    streamProtos[i].streamUseCase = streamStats.getStreamUseCase();
-                    streamProtos[i].colorSpace = streamStats.getColorSpace();
-
-                    if (CameraServiceProxy.DEBUG) {
-                        String histogramTypeName =
-                                cameraHistogramTypeToString(streamProtos[i].histogramType);
-                        Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width
-                                + ", height " + streamProtos[i].height
-                                + ", format " + streamProtos[i].format
-                                + ", maxPreviewFps " + streamStats.getMaxPreviewFps()
-                                + ", dataSpace " + streamProtos[i].dataSpace
-                                + ", usage " + streamProtos[i].usage
-                                + ", requestCount " + streamProtos[i].requestCount
-                                + ", errorCount " + streamProtos[i].errorCount
-                                + ", firstCaptureLatencyMillis "
-                                + streamProtos[i].firstCaptureLatencyMillis
-                                + ", maxHalBuffers " + streamProtos[i].maxHalBuffers
-                                + ", maxAppBuffers " + streamProtos[i].maxAppBuffers
-                                + ", histogramType " + histogramTypeName
-                                + ", histogramBins "
-                                + Arrays.toString(streamProtos[i].histogramBins)
-                                + ", histogramCounts "
-                                + Arrays.toString(streamProtos[i].histogramCounts)
-                                + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile
-                                + ", streamUseCase " + streamProtos[i].streamUseCase
-                                + ", colorSpace " + streamProtos[i].colorSpace);
-                    }
-                }
-            }
-            FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(),
-                    e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk,
-                    e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure,
-                    e.mRequestCount, e.mResultErrorCount, e.mDeviceError,
-                    streamCount, MessageNano.toByteArray(streamProtos[0]),
-                    MessageNano.toByteArray(streamProtos[1]),
-                    MessageNano.toByteArray(streamProtos[2]),
-                    MessageNano.toByteArray(streamProtos[3]),
-                    MessageNano.toByteArray(streamProtos[4]),
-                    e.mUserTag, e.mVideoStabilizationMode,
-                    e.mLogId, e.mSessionIndex,
-                    extensionType, extensionIsAdvanced, e.mUsedUltraWide,
-                    e.mUsedZoomOverride,
-                    e.mMostRequestedFpsRange.getLower(), e.mMostRequestedFpsRange.getUpper(),
-                    extensionCaptureFormat);
-        }
     }
 
     /**
@@ -1055,22 +1137,22 @@
      */
     int getUsageEventCount() {
         synchronized (mLock) {
-            return mCameraUsageHistory.size();
+            return mCameraEventHistory.size();
         }
     }
 
     /**
-     * Dump camera usage events to log.
+     * Dump camera events to log.
      * Package-private
      */
-    void dumpUsageEvents() {
+    void dumpCameraEvents() {
         synchronized(mLock) {
             // Randomize order of events so that it's not meaningful
-            Collections.shuffle(mCameraUsageHistory);
+            Collections.shuffle(mCameraEventHistory);
             mLogWriterService.execute(new EventWriterTask(
-                        new ArrayList<CameraUsageEvent>(mCameraUsageHistory)));
+                        new ArrayList(mCameraEventHistory)));
 
-            mCameraUsageHistory.clear();
+            mCameraEventHistory.clear();
         }
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -1288,7 +1370,7 @@
                             cameraId, facing, clientName, apiLevel, isNdk,
                             FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN,
                             latencyMs, sessionType, deviceError, logId, sessionIdx);
-                    mCameraUsageHistory.add(openEvent);
+                    mCameraEventHistory.add(openEvent);
                     break;
                 case CameraSessionStats.CAMERA_STATE_ACTIVE:
                     // Check current active camera IDs to see if this package is already talking to
@@ -1323,7 +1405,7 @@
                                 /*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false,
                                 /*usedZoomOverride*/false, new Range<Integer>(0, 0),
                                 new CameraExtensionSessionStats());
-                        mCameraUsageHistory.add(oldEvent);
+                        mCameraEventHistory.add(oldEvent);
                     }
                     break;
                 case CameraSessionStats.CAMERA_STATE_IDLE:
@@ -1335,7 +1417,7 @@
                                 resultErrorCount, deviceError, streamStats, userTag,
                                 videoStabilizationMode, usedUltraWide, usedZoomOverride,
                                 mostRequestedFpsRange, extSessionStats);
-                        mCameraUsageHistory.add(doneEvent);
+                        mCameraEventHistory.add(doneEvent);
                         // Do not double count device error
                         deviceError = false;
 
@@ -1362,11 +1444,11 @@
                                 cameraId, facing, clientName, apiLevel, isNdk,
                                 FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE,
                                 latencyMs, sessionType, deviceError, logId, sessionIdx);
-                        mCameraUsageHistory.add(closeEvent);
+                        mCameraEventHistory.add(closeEvent);
                     }
 
-                    if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
-                        dumpUsageEvents();
+                    if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) {
+                        dumpCameraEvents();
                     }
 
                     break;
@@ -1378,6 +1460,18 @@
         }
     }
 
+    private void updateFeatureCombinationQuery(CameraFeatureCombinationStats featureCombStats) {
+        synchronized (mLock) {
+            CameraFeatureCombinationQueryEvent e =
+                    new CameraFeatureCombinationQueryEvent(featureCombStats);
+            mCameraEventHistory.add(e);
+
+            if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) {
+                dumpCameraEvents();
+            }
+        }
+    }
+
     private void notifyNfcService(boolean enablePolling) {
         NfcManager nfcManager = mContext.getSystemService(NfcManager.class);
         if (nfcManager == null) {
@@ -1434,4 +1528,41 @@
         return "HISTOGRAM_TYPE_UNKNOWN";
     }
 
+    private static String cameraFeatureCombinationTypeToString(int featureCombinationType) {
+        switch (featureCombinationType) {
+            case CameraFeatureCombinationStats.QueryType.QUERY_FEATURE_COMBINATION:
+                return "QUERY_FEATURE_COMBINATION";
+            case CameraFeatureCombinationStats.QueryType.QUERY_SESSION_CHARACTERISTICS:
+                return "QUERY_SESSION_CHARACTERISTICS";
+            default:
+                break;
+        }
+        return "FEATURE_COMBINATION_TYPE_UNKNOWN";
+    }
+
+    private static String cameraFeatureCombinationToString(long featureCombination) {
+        StringBuilder combinationStr = new StringBuilder("{");
+        if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_60_FPS) != 0) {
+            combinationStr.append("60fps ");
+        }
+        if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_STABILIZATION)
+                != 0) {
+            combinationStr.append("stabilization ");
+        }
+        if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_HLG10) != 0) {
+            combinationStr.append("hlg10 ");
+        }
+        if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG) != 0) {
+            combinationStr.append("jpeg ");
+        }
+        if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG_R) != 0) {
+            combinationStr.append("jpeg_r ");
+        }
+        if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_4K) != 0) {
+            combinationStr.append("4k ");
+        }
+        combinationStr.append("}");
+
+        return combinationStr.toString();
+    }
 }
diff --git a/services/core/java/com/android/server/camera/CameraStatsJobService.java b/services/core/java/com/android/server/camera/CameraStatsJobService.java
index b8a6846..1227ca7 100644
--- a/services/core/java/com/android/server/camera/CameraStatsJobService.java
+++ b/services/core/java/com/android/server/camera/CameraStatsJobService.java
@@ -21,14 +21,13 @@
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.util.Slog;
 
-import java.util.concurrent.TimeUnit;
-
 import com.android.server.LocalServices;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * A JobService to periodically collect camera usage stats.
  */
@@ -50,7 +49,7 @@
             return false;
         }
 
-        serviceProxy.dumpUsageEvents();
+        serviceProxy.dumpCameraEvents();
         return false;
     }
 
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 4c3020f..0afca92 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -30,7 +30,6 @@
 import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IUriGrantsManager;
 import android.app.KeyguardManager;
@@ -48,9 +47,8 @@
 import android.content.IOnPrimaryClipChangedListener;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
@@ -1247,20 +1245,13 @@
 
     @GuardedBy("mLock")
     private void addActiveOwnerLocked(int uid, int deviceId, String pkg) {
-        final IPackageManager pm = AppGlobals.getPackageManager();
+        final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
         final int targetUserHandle = UserHandle.getCallingUserId();
         final long oldIdentity = Binder.clearCallingIdentity();
         try {
-            PackageInfo pi = pm.getPackageInfo(pkg, 0, targetUserHandle);
-            if (pi == null) {
-                throw new IllegalArgumentException("Unknown package " + pkg);
+            if (!pm.isSameApp(pkg, 0, uid, targetUserHandle)) {
+                throw new SecurityException("Calling uid " + uid + " does not own package " + pkg);
             }
-            if (!UserHandle.isSameApp(pi.applicationInfo.uid, uid)) {
-                throw new SecurityException("Calling uid " + uid
-                        + " does not own package " + pkg);
-            }
-        } catch (RemoteException e) {
-            // Can't happen; the package manager is in the same process
         } finally {
             Binder.restoreCallingIdentity(oldIdentity);
         }
diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
index 0812fd9..de7341d 100644
--- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
+++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java
@@ -21,6 +21,7 @@
 import android.app.assist.ActivityId;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
+import android.content.Intent;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.service.contentcapture.ActivityEvent.ActivityEventType;
@@ -40,6 +41,14 @@
     public abstract boolean isContentCaptureServiceForUser(int uid, @UserIdInt int userId);
 
     /**
+     * Notifies the intelligence service of new intent data associated with an activity start event.
+     *
+     * @return {@code false} if there was no service set for the given user
+     */
+    public abstract boolean sendActivityStartAssistData(@UserIdInt int userId,
+            @NonNull IBinder activityToken, @NonNull Intent intentData);
+
+    /**
      * Notifies the intelligence service of new assist data for the given activity.
      *
      * @return {@code false} if there was no service set for the given user
diff --git a/services/core/java/com/android/server/criticalevents/OWNERS b/services/core/java/com/android/server/criticalevents/OWNERS
index 9c3136c..7935bed 100644
--- a/services/core/java/com/android/server/criticalevents/OWNERS
+++ b/services/core/java/com/android/server/criticalevents/OWNERS
@@ -1,2 +1 @@
 benmiles@google.com
-gaillard@google.com
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 1949e6f..7b5cff7 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -54,7 +54,6 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
 import com.android.server.display.brightness.BrightnessEvent;
-import com.android.server.display.brightness.clamper.BrightnessClamperController;
 import com.android.server.display.config.HysteresisLevels;
 import com.android.server.display.feature.DisplayManagerFlags;
 
@@ -256,7 +255,6 @@
 
     // Controls Brightness range (including High Brightness Mode).
     private final BrightnessRangeController mBrightnessRangeController;
-    private final BrightnessClamperController mBrightnessClamperController;
 
     // Throttles (caps) maximum allowed brightness
     private final BrightnessThrottler mBrightnessThrottler;
@@ -295,7 +293,6 @@
             BrightnessRangeController brightnessModeController,
             BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
             int ambientLightHorizonLong, float userLux, float userNits,
-            BrightnessClamperController brightnessClamperController,
             DisplayManagerFlags displayManagerFlags) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor,
                 brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax,
@@ -306,7 +303,7 @@
                 screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                 screenBrightnessThresholdsIdle, context, brightnessModeController,
                 brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
-                userNits, brightnessClamperController, displayManagerFlags
+                userNits, displayManagerFlags
         );
     }
 
@@ -325,7 +322,6 @@
             BrightnessRangeController brightnessRangeController,
             BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
             int ambientLightHorizonLong, float userLux, float userNits,
-            BrightnessClamperController brightnessClamperController,
             DisplayManagerFlags displayManagerFlags) {
         mInjector = injector;
         mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness());
@@ -370,7 +366,6 @@
         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
         mBrightnessRangeController = brightnessRangeController;
-        mBrightnessClamperController = brightnessClamperController;
         mBrightnessThrottler = brightnessThrottler;
         mBrightnessMappingStrategyMap = brightnessMappingStrategyMap;
         mDisplayManagerFlags = displayManagerFlags;
@@ -771,7 +766,6 @@
                     mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
         }
         mBrightnessRangeController.onAmbientLuxChange(mAmbientLux);
-        mBrightnessClamperController.onAmbientLuxChange(mAmbientLux);
 
         // If the short term model was invalidated and the change is drastic enough, reset it.
         mShortTermModel.maybeReset(mAmbientLux);
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index eeacc53..e4db634 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -230,6 +230,16 @@
  *              <nits>55.2</nits>
  *            </displayBrightnessPoint>
  *          </blockingZoneThreshold>
+ *          <supportedModes>
+ *            <point>
+ *              <first>60</first>   // refresh rate
+ *              <second>60</second> // vsync
+ *            </point>
+ *            <point>
+ *              <first>120</first>    // refresh rate
+ *              <second>120</second> // vsync
+ *            </point>
+ *          </supportedModes>
  *        </lowerBlockingZoneConfigs>
  *        <higherBlockingZoneConfigs>
  *          <defaultRefreshRate>90</defaultRefreshRate>
@@ -244,6 +254,16 @@
  *            </displayBrightnessPoint>
  *          </blockingZoneThreshold>
  *        </higherBlockingZoneConfigs>
+ *        <lowPowerSupportedModes>
+ *          <point>
+ *            <first>60</first>   // refresh rate
+ *            <second>60</second> // vsync
+ *          </point>
+ *          <point>
+ *            <first>60</first>    // refresh rate
+ *            <second>240</second> // vsync
+ *          </point>
+ *        </lowPowerSupportedModes>
  *      </refreshRate>
  *
  *      <highBrightnessMode enabled="true">
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index b1b1dba..93bd926 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -212,24 +212,46 @@
     public static final int TOUCH_VIRTUAL = 3;
 
     /**
-     * Diff result: The {@link #state} or {@link #committedState} fields differ.
-     */
-    public static final int DIFF_STATE = 1 << 0;
-
-    /**
      * Diff result: Other fields differ.
      */
-    public static final int DIFF_OTHER = 1 << 1;
+    public static final int DIFF_OTHER = 1 << 0;
+
+    /**
+     * Diff result: The {@link #state} or {@link #committedState} fields differ.
+     */
+    public static final int DIFF_STATE = 1 << 1;
+
+    /**
+     * Diff result: The committed state differs. Note this is slightly different from the state,
+     * which is what most of the device should care about.
+     */
+    public static final int DIFF_COMMITTED_STATE = 1 << 2;
 
     /**
      * Diff result: The color mode fields differ.
      */
-    public static final int DIFF_COLOR_MODE = 1 << 2;
+    public static final int DIFF_COLOR_MODE = 1 << 3;
 
     /**
      * Diff result: The hdr/sdr ratio differs
      */
-    public static final int DIFF_HDR_SDR_RATIO = 1 << 3;
+    public static final int DIFF_HDR_SDR_RATIO = 1 << 4;
+
+    /**
+     * Diff result: The rotation differs
+     */
+    public static final int DIFF_ROTATION = 1 << 5;
+
+    /**
+     * Diff result: The render timings. Note this could be any of {@link #renderFrameRate},
+     * {@link #presentationDeadlineNanos}, or {@link #appVsyncOffsetNanos}.
+     */
+    public static final int DIFF_RENDER_TIMINGS = 1 << 6;
+
+    /**
+     * Diff result: The mode ID differs.
+     */
+    public static final int DIFF_MODE_ID = 1 << 7;
 
     /**
      * Diff result: Catch-all for "everything changed"
@@ -462,21 +484,33 @@
      */
     public int diff(DisplayDeviceInfo other) {
         int diff = 0;
-        if (state != other.state || committedState != other.committedState) {
+        if (state != other.state) {
             diff |= DIFF_STATE;
         }
+        if (committedState != other.committedState) {
+            diff |= DIFF_COMMITTED_STATE;
+        }
         if (colorMode != other.colorMode) {
             diff |= DIFF_COLOR_MODE;
         }
         if (!BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)) {
             diff |= DIFF_HDR_SDR_RATIO;
         }
+        if (rotation != other.rotation) {
+            diff |= DIFF_ROTATION;
+        }
+        if (renderFrameRate != other.renderFrameRate
+                || presentationDeadlineNanos != other.presentationDeadlineNanos
+                || appVsyncOffsetNanos != other.appVsyncOffsetNanos) {
+            diff |= DIFF_RENDER_TIMINGS;
+        }
+        if (modeId != other.modeId) {
+            diff |= DIFF_MODE_ID;
+        }
         if (!Objects.equals(name, other.name)
                 || !Objects.equals(uniqueId, other.uniqueId)
                 || width != other.width
                 || height != other.height
-                || modeId != other.modeId
-                || renderFrameRate != other.renderFrameRate
                 || defaultModeId != other.defaultModeId
                 || userPreferredModeId != other.userPreferredModeId
                 || !Arrays.equals(supportedModes, other.supportedModes)
@@ -487,12 +521,9 @@
                 || densityDpi != other.densityDpi
                 || xDpi != other.xDpi
                 || yDpi != other.yDpi
-                || appVsyncOffsetNanos != other.appVsyncOffsetNanos
-                || presentationDeadlineNanos != other.presentationDeadlineNanos
                 || flags != other.flags
                 || !Objects.equals(displayCutout, other.displayCutout)
                 || touch != other.touch
-                || rotation != other.rotation
                 || type != other.type
                 || !Objects.equals(address, other.address)
                 || !Objects.equals(deviceProductInfo, other.deviceProductInfo)
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 6164154..086f8a9 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -21,6 +21,7 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayAddress;
+import android.view.Surface;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.display.DisplayManagerService.SyncRoot;
@@ -179,6 +180,20 @@
             if (diff == DisplayDeviceInfo.DIFF_STATE) {
                 Slog.i(TAG, "Display device changed state: \"" + info.name
                         + "\", " + Display.stateToString(info.state));
+            } else if (diff == DisplayDeviceInfo.DIFF_ROTATION) {
+                Slog.i(TAG, "Display device rotated: \"" + info.name
+                        + "\", " + Surface.rotationToString(info.rotation));
+            } else if (diff
+                    == (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS)) {
+                Slog.i(TAG, "Display device changed render timings: \"" + info.name
+                        + "\", renderFrameRate=" + info.renderFrameRate
+                        + ", presentationDeadlineNanos=" + info.presentationDeadlineNanos
+                        + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos);
+            } else if (diff == DisplayDeviceInfo.DIFF_COMMITTED_STATE) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Display device changed committed state: \"" + info.name
+                            + "\", " + Display.stateToString(info.committedState));
+                }
             } else if (diff != DisplayDeviceInfo.DIFF_HDR_SDR_RATIO) {
                 Slog.i(TAG, "Display device changed: " + info);
             }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5fd0253..2d5f38e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -3406,6 +3406,31 @@
         }
     }
 
+    boolean requestDisplayPower(int displayId, boolean on) {
+        synchronized (mSyncRoot) {
+            final var display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+            if (display == null) {
+                Slog.w(TAG, "requestDisplayPower: Cannot find a display with displayId="
+                        + displayId);
+                return false;
+            }
+            final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId);
+            var runnable = display.getPrimaryDisplayDeviceLocked().requestDisplayStateLocked(
+                    on ? Display.STATE_ON : Display.STATE_OFF,
+                    on ? brightnessPair.brightness : PowerManager.BRIGHTNESS_OFF_FLOAT,
+                    brightnessPair.sdrBrightness,
+                    display.getDisplayOffloadSessionLocked());
+            if (runnable == null) {
+                Slog.w(TAG, "requestDisplayPower: Cannot update the power state to ON=" + on
+                        + " for a display with displayId=" + displayId + ", runnable is null");
+                return false;
+            }
+            runnable.run();
+            Slog.i(TAG, "requestDisplayPower(displayId=" + displayId + ", on=" + on + ")");
+        }
+        return true;
+    }
+
     /**
      * This is the object that everything in the display manager locks on.
      * We make it an inner class within the {@link DisplayManagerService} to so that it is
@@ -4629,6 +4654,12 @@
             DisplayManagerService.this.enableConnectedDisplay(displayId, false);
         }
 
+        @EnforcePermission(MANAGE_DISPLAYS)
+        public boolean requestDisplayPower(int displayId, boolean on) {
+            requestDisplayPower_enforcePermission();
+            return DisplayManagerService.this.requestDisplayPower(displayId, on);
+        }
+
         @EnforcePermission(RESTRICT_DISPLAY_MODES)
         @Override // Binder call
         public void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 8c39d7d..d973b71 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -106,6 +106,10 @@
                 return setDisplayEnabled(true);
             case "disable-display":
                 return setDisplayEnabled(false);
+            case "power-on":
+                return requestDisplayPower(true);
+            case "power-off":
+                return requestDisplayPower(false);
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -592,4 +596,21 @@
         mService.enableConnectedDisplay(displayId, enable);
         return 0;
     }
+
+    private int requestDisplayPower(boolean enable) {
+        final String displayIdText = getNextArg();
+        if (displayIdText == null) {
+            getErrPrintWriter().println("Error: no displayId specified");
+            return 1;
+        }
+        final int displayId;
+        try {
+            displayId = Integer.parseInt(displayIdText);
+        } catch (NumberFormatException e) {
+            getErrPrintWriter().println("Error: invalid displayId: '" + displayIdText + "'");
+            return 1;
+        }
+        mService.requestDisplayPower(displayId, enable);
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 60d0353..65a729a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -159,7 +159,7 @@
     private static final int MSG_STATSD_HBM_BRIGHTNESS = 11;
     private static final int MSG_SWITCH_USER = 12;
     private static final int MSG_BOOT_COMPLETED = 13;
-    private static final int MSG_SET_DWBC_STRONG_MODE = 14;
+    private static final int MSG_SWITCH_AUTOBRIGHTNESS_MODE = 14;
     private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15;
     private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16;
     private static final int MSG_SET_BRIGHTNESS_FROM_OFFLOAD = 17;
@@ -587,7 +587,7 @@
                         mUniqueDisplayId,
                         mThermalBrightnessThrottlingDataId,
                         logicalDisplay.getPowerThrottlingDataIdLocked(),
-                        mDisplayDeviceConfig), mContext, flags);
+                        mDisplayDeviceConfig), mContext, flags, mSensorManager);
         // Seed the cached brightness
         saveBrightnessInfo(getScreenBrightnessSetting());
         mAutomaticBrightnessStrategy =
@@ -1184,15 +1184,9 @@
     @Override
     public void setAutomaticScreenBrightnessMode(
             @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
-        boolean isIdle = mode == AUTO_BRIGHTNESS_MODE_IDLE;
-        if (mAutomaticBrightnessController != null) {
-            // Set sendUpdate to true to make sure that updatePowerState() gets called
-            mAutomaticBrightnessController.switchMode(mode, /* sendUpdate= */ true);
-            setAnimatorRampSpeeds(isIdle);
-        }
         Message msg = mHandler.obtainMessage();
-        msg.what = MSG_SET_DWBC_STRONG_MODE;
-        msg.arg1 = isIdle ? 1 : 0;
+        msg.what = MSG_SWITCH_AUTOBRIGHTNESS_MODE;
+        msg.arg1 = mode;
         mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
     }
 
@@ -1428,7 +1422,6 @@
                         : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 
         mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessState);
-        mBrightnessClamperController.setAutoBrightnessState(autoBrightnessState);
 
         boolean updateScreenBrightnessSetting =
                 displayBrightnessState.shouldUpdateScreenBrightnessSetting();
@@ -1555,7 +1548,7 @@
         // we broadcast this change through setting.
         final float unthrottledBrightnessState = brightnessState;
         DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest,
-                brightnessState, slowChange);
+                brightnessState, slowChange, /* displayState= */ state);
 
         brightnessState = clampedState.getBrightness();
         slowChange = clampedState.isSlowChange();
@@ -1866,7 +1859,7 @@
 
     private void setDwbcStrongMode(int arg) {
         if (mDisplayWhiteBalanceController != null) {
-            final boolean isIdle = (arg == 1);
+            final boolean isIdle = (arg == AUTO_BRIGHTNESS_MODE_IDLE);
             mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle);
         }
     }
@@ -2484,7 +2477,6 @@
     public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
             boolean slowChange) {
         mBrightnessRangeController.onAmbientLuxChange(ambientLux);
-        mBrightnessClamperController.onAmbientLuxChange(ambientLux);
         if (nits == BrightnessMappingStrategy.INVALID_NITS) {
             mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange);
         } else {
@@ -3032,7 +3024,12 @@
                     updatePowerState();
                     break;
 
-                case MSG_SET_DWBC_STRONG_MODE:
+                case MSG_SWITCH_AUTOBRIGHTNESS_MODE:
+                    boolean isIdle = msg.arg1 == AUTO_BRIGHTNESS_MODE_IDLE;
+                    if (mAutomaticBrightnessController != null) {
+                        mAutomaticBrightnessController.switchMode(msg.arg1, /* sendUpdate= */ true);
+                        setAnimatorRampSpeeds(isIdle);
+                    }
                     setDwbcStrongMode(msg.arg1);
                     break;
 
@@ -3195,7 +3192,7 @@
                     screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                     screenBrightnessThresholdsIdle, context, brightnessModeController,
                     brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
-                    userNits, brightnessClamperController, displayManagerFlags);
+                    userNits, displayManagerFlags);
         }
 
         BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context,
@@ -3244,10 +3241,10 @@
         BrightnessClamperController getBrightnessClamperController(Handler handler,
                 BrightnessClamperController.ClamperChangeListener clamperChangeListener,
                 BrightnessClamperController.DisplayDeviceData data, Context context,
-                DisplayManagerFlags flags) {
+                DisplayManagerFlags flags, SensorManager sensorManager) {
 
             return new BrightnessClamperController(handler, clamperChangeListener, data, context,
-                    flags);
+                    flags, sensorManager);
         }
 
         DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
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 11ef577..101ad30 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
@@ -16,21 +16,30 @@
 
 package com.android.server.display.brightness.clamper;
 
+import static android.view.Display.STATE_ON;
+
 import static com.android.server.display.brightness.clamper.BrightnessClamper.Type;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfigInterface;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.DisplayDeviceConfig;
@@ -41,20 +50,30 @@
 import com.android.server.display.config.SensorData;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.display.utils.AmbientFilter;
+import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.display.utils.DebugUtils;
+import com.android.server.display.utils.SensorUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Clampers controller, all in DisplayControllerHandler
  */
 public class BrightnessClamperController {
     private static final String TAG = "BrightnessClamperController";
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.BrightnessClamperController DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
+    public static final float INVALID_LUX = -1f;
 
     private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
     private final Handler mHandler;
+    private final SensorManager mSensorManager;
     private final ClamperChangeListener mClamperChangeListenerExternal;
     private final Executor mExecutor;
     private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers;
@@ -66,24 +85,55 @@
     private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
     @Nullable
     private Type mClamperType = null;
-    private int mAutoBrightnessState = -1;
+    private final SensorEventListener mLightSensorListener;
+    private Sensor mRegisteredLightSensor = null;
+    private Sensor mLightSensor;
+    private String mLightSensorType;
+    private String mLightSensorName;
+    private AmbientFilter mAmbientFilter;
+    private final DisplayDeviceConfig mDisplayDeviceConfig;
+    private final Resources mResources;
+    private final int mLightSensorRate;
 
+    private final Injector mInjector;
     private boolean mClamperApplied = false;
 
     public BrightnessClamperController(Handler handler,
             ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
-            DisplayManagerFlags flags) {
-        this(new Injector(), handler, clamperChangeListener, data, context, flags);
+            DisplayManagerFlags flags, SensorManager sensorManager) {
+        this(null, handler, clamperChangeListener, data, context, flags, sensorManager);
     }
 
     @VisibleForTesting
     BrightnessClamperController(Injector injector, Handler handler,
             ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
-            DisplayManagerFlags flags) {
-        mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+            DisplayManagerFlags flags, SensorManager sensorManager) {
+        mInjector = injector == null ? new Injector() : injector;
+        mDeviceConfigParameterProvider = mInjector.getDeviceConfigParameterProvider();
         mHandler = handler;
+        mSensorManager = sensorManager;
+        mDisplayDeviceConfig = data.mDisplayDeviceConfig;
+        mLightSensorListener = new SensorEventListener() {
+            @Override
+            public void onSensorChanged(SensorEvent event) {
+                long now = SystemClock.elapsedRealtime();
+                mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp),
+                        event.values[0]);
+                final float lux = mAmbientFilter.getEstimate(now);
+                mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux));
+            }
+
+            @Override
+            public void onAccuracyChanged(Sensor sensor, int accuracy) {
+                // unused
+            }
+        };
+
         mClamperChangeListenerExternal = clamperChangeListener;
         mExecutor = new HandlerExecutor(handler);
+        mResources = context.getResources();
+        mLightSensorRate = context.getResources().getInteger(
+                R.integer.config_autoBrightnessLightSensorRate);
 
         Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap;
 
@@ -93,10 +143,10 @@
             }
         };
 
-        mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
+        mClampers = mInjector.getClampers(handler, clamperChangeListenerInternal, data, flags,
                 context);
-        mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener,
-                data.mDisplayDeviceConfig);
+        mModifiers = mInjector.getModifiers(flags, context, handler, clamperChangeListener,
+                data.mDisplayDeviceConfig, mSensorManager);
         mOnPropertiesChangedListener =
                 properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
         start();
@@ -114,7 +164,7 @@
      * Called in DisplayControllerHandler
      */
     public DisplayBrightnessState clamp(DisplayManagerInternal.DisplayPowerRequest request,
-            float brightnessValue, boolean slowChange) {
+            float brightnessValue, boolean slowChange, int displayState) {
         float cappedBrightness = Math.min(brightnessValue, mBrightnessCap);
 
         DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder();
@@ -133,6 +183,12 @@
             mClamperApplied = false;
         }
 
+        if (displayState != STATE_ON) {
+            unregisterSensorListener();
+        } else {
+            maybeRegisterLightSensor();
+        }
+
         for (int i = 0; i < mModifiers.size(); i++) {
             mModifiers.get(i).apply(request, builder);
         }
@@ -175,6 +231,8 @@
         writer.println("  mBrightnessCap: " + mBrightnessCap);
         writer.println("  mClamperType: " + mClamperType);
         writer.println("  mClamperApplied: " + mClamperApplied);
+        writer.println("  mLightSensor=" + mLightSensor);
+        writer.println("  mRegisteredLightSensor=" + mRegisteredLightSensor);
         IndentingPrintWriter ipw = new IndentingPrintWriter(writer, "    ");
         mClampers.forEach(clamper -> clamper.dump(ipw));
         mModifiers.forEach(modifier -> modifier.dump(ipw));
@@ -191,26 +249,6 @@
         mModifiers.forEach(BrightnessStateModifier::stop);
     }
 
-    /**
-     * Notifies modifiers that ambient lux has changed.
-     * @param ambientLux current lux, debounced
-     */
-    public void onAmbientLuxChange(float ambientLux) {
-        mModifiers.forEach(modifier -> modifier.onAmbientLuxChange(ambientLux));
-    }
-
-    /**
-     * Sets the autobrightness state for clampers that need to be aware of the state.
-     * @param state autobrightness state
-     */
-    public void setAutoBrightnessState(int state) {
-        if (state == mAutoBrightnessState) {
-            return;
-        }
-        mModifiers.forEach(modifier -> modifier.setAutoBrightnessState(state));
-        mAutoBrightnessState = state;
-        recalculateBrightnessCap();
-    }
 
     // Called in DisplayControllerHandler
     private void recalculateBrightnessCap() {
@@ -243,6 +281,10 @@
         if (!mClampers.isEmpty()) {
             mDeviceConfigParameterProvider.addOnPropertiesChangedListener(
                     mExecutor, mOnPropertiesChangedListener);
+            reloadLightSensorData(mDisplayDeviceConfig);
+            mLightSensor = mInjector.getLightSensor(
+                    mSensorManager, mLightSensorType, mLightSensorName);
+            maybeRegisterLightSensor();
         }
     }
 
@@ -281,7 +323,7 @@
 
         List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
                 Handler handler, ClamperChangeListener listener,
-                DisplayDeviceConfig displayDeviceConfig) {
+                DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) {
             List<BrightnessStateModifier> modifiers = new ArrayList<>();
             modifiers.add(new DisplayDimModifier(context));
             modifiers.add(new BrightnessLowPowerModeModifier());
@@ -292,13 +334,19 @@
             }
             return modifiers;
         }
+
+        Sensor getLightSensor(SensorManager sensorManager, String type, String name) {
+            return SensorUtils.findSensor(sensorManager, type,
+                    name, Sensor.TYPE_LIGHT);
+        }
+
     }
 
     /**
      * Config Data for clampers
      */
     public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
-                BrightnessPowerClamper.PowerData,
+            BrightnessPowerClamper.PowerData,
             BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
         @NonNull
         private final String mUniqueDisplayId;
@@ -368,4 +416,51 @@
             return mDisplayDeviceConfig.getTempSensor();
         }
     }
+
+    private void maybeRegisterLightSensor() {
+        if (mModifiers.stream().noneMatch(BrightnessStateModifier::shouldListenToLightSensor)) {
+            return;
+        }
+
+        if (mRegisteredLightSensor == mLightSensor) {
+            return;
+        }
+
+        if (mRegisteredLightSensor != null) {
+            unregisterSensorListener();
+        }
+
+        mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, mResources);
+        mSensorManager.registerListener(mLightSensorListener,
+                mLightSensor, mLightSensorRate * 1000, mHandler);
+        mRegisteredLightSensor = mLightSensor;
+
+        if (DEBUG) {
+            Slog.d(TAG, "maybeRegisterLightSensor");
+        }
+    }
+
+    private void unregisterSensorListener() {
+        mSensorManager.unregisterListener(mLightSensorListener);
+        mRegisteredLightSensor = null;
+        mModifiers.forEach(mModifier -> mModifier.setAmbientLux(INVALID_LUX)); // set lux to invalid
+        if (DEBUG) {
+            Slog.d(TAG, "unregisterSensorListener");
+        }
+    }
+
+    private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) {
+        // The displayDeviceConfig (ddc) contains display specific preferences. When loaded,
+        // it naturally falls back to the global config.xml.
+        if (displayDeviceConfig != null
+                && displayDeviceConfig.getAmbientLightSensor() != null) {
+            // This covers both the ddc and the config.xml fallback
+            mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type;
+            mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name;
+        } else if (mLightSensorName == null && mLightSensorType == null) {
+            mLightSensorType = mResources.getString(
+                    com.android.internal.R.string.config_displayLightSensorType);
+            mLightSensorName = "";
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
index 7ba4a4d..951980a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java
@@ -16,7 +16,6 @@
 
 package com.android.server.display.brightness.clamper;
 
-import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -30,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.display.BrightnessSynchronizer;
+import com.android.server.display.BrightnessMappingStrategy;
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.DisplayDeviceConfig;
 import com.android.server.display.brightness.BrightnessReason;
@@ -56,7 +56,6 @@
     private float mBrightnessLowerBound;
     private float mMinNitsAllowed;
     private boolean mIsActive;
-    private boolean mAutoBrightnessEnabled;
     private float mAmbientLux;
     private final DisplayDeviceConfig mDisplayDeviceConfig;
 
@@ -87,15 +86,15 @@
                 mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS,
                 /* def= */ MIN_NITS_DEFAULT, userId);
 
-        boolean isActive = isSettingEnabled() && mAutoBrightnessEnabled;
-
-        float luxBasedNitsLowerBound = mDisplayDeviceConfig.getMinNitsFromLux(mAmbientLux);
+        boolean isActive = isSettingEnabled()
+                && mAmbientLux != BrightnessMappingStrategy.INVALID_LUX;
 
         final int reason;
         float minNitsAllowed = -1f; // undefined, if setting is off.
         final float minBrightnessAllowed;
 
         if (isActive) {
+            float luxBasedNitsLowerBound = mDisplayDeviceConfig.getMinNitsFromLux(mAmbientLux);
             minNitsAllowed = Math.max(settingNitsLowerBound,
                     luxBasedNitsLowerBound);
             minBrightnessAllowed = getBrightnessFromNits(minNitsAllowed);
@@ -127,6 +126,12 @@
     }
 
     @VisibleForTesting
+    public void setAmbientLux(float lux) {
+        mAmbientLux = lux;
+        recalculateLowerBound();
+    }
+
+    @VisibleForTesting
     public boolean isActive() {
         return mIsActive;
     }
@@ -164,10 +169,10 @@
     @Override
     public void apply(DisplayManagerInternal.DisplayPowerRequest request,
             DisplayBrightnessState.Builder stateBuilder) {
+
         stateBuilder.setMinBrightness(mBrightnessLowerBound);
         float boundedBrightness = Math.max(mBrightnessLowerBound, stateBuilder.getBrightness());
         stateBuilder.setBrightness(boundedBrightness);
-
         if (BrightnessSynchronizer.floatEquals(stateBuilder.getBrightness(),
                 mBrightnessLowerBound)) {
             stateBuilder.getBrightnessReason().addModifier(mReason);
@@ -180,14 +185,8 @@
     }
 
     @Override
-    public void onAmbientLuxChange(float ambientLux) {
-        mAmbientLux = ambientLux;
-        recalculateLowerBound();
-    }
-
-    @Override
-    public void setAutoBrightnessState(int state) {
-        mAutoBrightnessEnabled = state == AUTO_BRIGHTNESS_ENABLED;
+    public boolean shouldListenToLightSensor() {
+        return isSettingEnabled();
     }
 
     @Override
@@ -217,6 +216,7 @@
     }
 
     private final class SettingsObserver extends ContentObserver {
+
         SettingsObserver(Handler handler) {
             super(handler);
             mContentResolver.registerContentObserver(
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
index b478952..5661ede 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java
@@ -51,4 +51,14 @@
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
         super.dump(ipw);
     }
+
+    @Override
+    public boolean shouldListenToLightSensor() {
+        return false;
+    }
+
+    @Override
+    public void setAmbientLux(float lux) {
+        // unused
+    }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
index db5a524d..be8fa5a 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java
@@ -68,14 +68,4 @@
     public void stop() {
         // do nothing
     }
-
-    @Override
-    public void onAmbientLuxChange(float ambientLux) {
-        // do nothing
-    }
-
-    @Override
-    public void setAutoBrightnessState(int state) {
-        // do nothing
-    }
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
index 1606159c..fd40ce3 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java
@@ -44,14 +44,15 @@
     void stop();
 
     /**
-     * Allows modifiers to react to ambient lux changes.
-     * @param ambientLux current debounced lux.
+     *
+     * @return whether the brightness state modifier needs to listen to the ambient lux in order to
+     * calculate its bounds.
      */
-    void onAmbientLuxChange(float ambientLux);
+    boolean shouldListenToLightSensor();
 
     /**
-     * Sets the autobrightness state for clampers that need to be aware of the state.
-     * @param state autobrightness state
+     * Current ambient lux
+     * @param lux - ambient lux
      */
-    void setAutoBrightnessState(int state);
+    void setAmbientLux(float lux);
 }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
index 4ff7bdb..ab880bf 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java
@@ -76,4 +76,14 @@
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
         super.dump(ipw);
     }
+
+    @Override
+    public boolean shouldListenToLightSensor() {
+        return false;
+    }
+
+    @Override
+    public void setAmbientLux(float lux) {
+        // unused
+    }
 }
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 2b5241f..b43b35b 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
@@ -102,6 +102,9 @@
 
     private DisplayManagerFlags mDisplayManagerFlags;
 
+    // Indicates if the current auto-brightness should be ramped up or down slowly.
+    private boolean mIsSlowChange;
+
     @VisibleForTesting
     AutomaticBrightnessStrategy(Context context, int displayId, Injector injector,
             DisplayManagerFlags displayManagerFlags) {
@@ -172,6 +175,11 @@
                 isValid = true;
             }
         }
+
+        // A change is slow when the auto-brightness was already applied, and there are no new
+        // auto-brightness adjustments from an external client(e.g. Moving the slider). As such,
+        // it is important to record this value before applying the current auto-brightness.
+        mIsSlowChange = hasAppliedAutoBrightness() && !getAutoBrightnessAdjustmentChanged();
         setAutoBrightnessApplied(isValid);
         return isValid;
     }
@@ -284,8 +292,7 @@
                 .setSdrBrightness(brightness)
                 .setBrightnessReason(brightnessReason)
                 .setDisplayBrightnessStrategyName(getName())
-                .setIsSlowChange(hasAppliedAutoBrightness()
-                        && !getAutoBrightnessAdjustmentChanged())
+                .setIsSlowChange(mIsSlowChange)
                 .setBrightnessEvent(brightnessEvent)
                 .setBrightnessAdjustmentFlag(mAutoBrightnessAdjustmentReasonsFlags)
                 .setShouldUpdateScreenBrightnessSetting(
diff --git a/services/core/java/com/android/server/display/config/RefreshRateData.java b/services/core/java/com/android/server/display/config/RefreshRateData.java
index d7ed904..f769a89 100644
--- a/services/core/java/com/android/server/display/config/RefreshRateData.java
+++ b/services/core/java/com/android/server/display/config/RefreshRateData.java
@@ -64,18 +64,22 @@
 
     public final List<SupportedModeData> lowPowerSupportedModes;
 
+    public final List<SupportedModeData> lowLightBlockingZoneSupportedModes;
+
     @VisibleForTesting
     public RefreshRateData(int defaultRefreshRate, int defaultPeakRefreshRate,
             int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight,
-            List<SupportedModeData> lowPowerSupportedModes) {
+            List<SupportedModeData> lowPowerSupportedModes,
+            List<SupportedModeData> lowLightBlockingZoneSupportedModes) {
         this.defaultRefreshRate = defaultRefreshRate;
         this.defaultPeakRefreshRate = defaultPeakRefreshRate;
         this.defaultRefreshRateInHbmHdr = defaultRefreshRateInHbmHdr;
         this.defaultRefreshRateInHbmSunlight = defaultRefreshRateInHbmSunlight;
         this.lowPowerSupportedModes = Collections.unmodifiableList(lowPowerSupportedModes);
+        this.lowLightBlockingZoneSupportedModes =
+                Collections.unmodifiableList(lowLightBlockingZoneSupportedModes);
     }
 
-
     @Override
     public String toString() {
         return "RefreshRateData {"
@@ -84,6 +88,7 @@
                 + ", defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
                 + ", defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
                 + ", lowPowerSupportedModes=" + lowPowerSupportedModes
+                + ", lowLightBlockingZoneSupportedModes=" + lowLightBlockingZoneSupportedModes
                 + "} ";
     }
 
@@ -100,13 +105,19 @@
         int defaultRefreshRateInHbmSunlight = loadDefaultRefreshRateInHbmSunlight(
                 refreshRateConfigs, resources);
 
-        NonNegativeFloatToFloatMap modes =
+        NonNegativeFloatToFloatMap lowPowerModes =
                 refreshRateConfigs == null ? null : refreshRateConfigs.getLowPowerSupportedModes();
-        List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(modes);
+        List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(lowPowerModes);
+
+        BlockingZoneConfig lowerZoneConfig = refreshRateConfigs == null ? null
+                : refreshRateConfigs.getLowerBlockingZoneConfigs();
+        NonNegativeFloatToFloatMap lowerZoneModes =
+                lowerZoneConfig == null ? null : lowerZoneConfig.getSupportedModes();
+        List<SupportedModeData> lowLightSupportedModes = SupportedModeData.load(lowerZoneModes);
 
         return new RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
                 defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
-                lowPowerSupportedModes);
+                lowPowerSupportedModes, lowLightSupportedModes);
     }
 
     private static int loadDefaultRefreshRate(
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index d519748..d610f08 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -2157,8 +2157,19 @@
             }
         }
 
+        private boolean hasLowLightVrrConfig() {
+            DisplayDeviceConfig config;
+            synchronized (mLock) {
+                config = mDefaultDisplayDeviceConfig;
+            }
+            return mVsyncLowLightBlockingVoteEnabled
+                    && config != null
+                    && config.isVrrSupportEnabled()
+                    && !config.getRefreshRateData().lowLightBlockingZoneSupportedModes.isEmpty();
+        }
+
         private void restartObserver() {
-            if (mRefreshRateInLowZone > 0) {
+            if (mRefreshRateInLowZone > 0 || hasLowLightVrrConfig()) {
                 mShouldObserveDisplayLowChange = hasValidThreshold(
                         mLowDisplayBrightnessThresholds);
                 mShouldObserveAmbientLowChange = hasValidThreshold(
@@ -2300,6 +2311,7 @@
             return false;
         }
 
+        @GuardedBy("mLock")
         private void onBrightnessChangedLocked() {
             if (!mRefreshRateChangeable || mLowPowerModeEnabled) {
                 return;
@@ -2315,8 +2327,14 @@
 
             boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
             if (insideLowZone) {
-                refreshRateVote =
-                        Vote.forPhysicalRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+                if (hasLowLightVrrConfig()) {
+                    refreshRateVote = Vote.forSupportedRefreshRates(mDefaultDisplayDeviceConfig
+                            .getRefreshRateData().lowLightBlockingZoneSupportedModes);
+                } else {
+                    refreshRateVote = Vote.forPhysicalRefreshRates(
+                            mRefreshRateInLowZone, mRefreshRateInLowZone);
+                    refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
+                }
                 if (mLowZoneRefreshRateForThermals != null) {
                     RefreshRateRange range = SkinThermalStatusObserver
                             .findBestMatchingRefreshRateRange(mThermalStatus,
@@ -2326,18 +2344,6 @@
                                 Vote.forPhysicalRefreshRates(range.min, range.max);
                     }
                 }
-
-                if (mVsyncLowLightBlockingVoteEnabled
-                        && isVrrSupportedLocked(Display.DEFAULT_DISPLAY)) {
-                    refreshRateSwitchingVote = Vote.forSupportedRefreshRatesAndDisableSwitching(
-                            List.of(
-                                    new SupportedRefreshRatesVote.RefreshRates(
-                                            /* peakRefreshRate= */ 60f, /* vsyncRate= */ 60f),
-                                    new SupportedRefreshRatesVote.RefreshRates(
-                                            /* peakRefreshRate= */120f, /* vsyncRate= */ 120f)));
-                } else {
-                    refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
-                }
             }
 
             boolean insideHighZone = hasValidHighZone()
@@ -2368,7 +2374,7 @@
         }
 
         private boolean hasValidLowZone() {
-            return mRefreshRateInLowZone > 0
+            return (mRefreshRateInLowZone > 0 || hasLowLightVrrConfig())
                     && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
         }
 
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index 1ec469c..7cbdd13 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -16,10 +16,13 @@
 
 package com.android.server.display.mode;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 
 import com.android.server.display.config.SupportedModeData;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -132,15 +135,40 @@
     // to function, so this needs to be the highest priority of all votes.
     int PRIORITY_UDFPS = 20;
 
+    @IntDef(prefix = { "PRIORITY_" }, value = {
+            PRIORITY_DEFAULT_RENDER_FRAME_RATE,
+            PRIORITY_FLICKER_REFRESH_RATE,
+            PRIORITY_HIGH_BRIGHTNESS_MODE,
+            PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
+            PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE,
+            PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
+            PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+            PRIORITY_APP_REQUEST_SIZE,
+            PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
+            PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+            PRIORITY_SYNCHRONIZED_REFRESH_RATE,
+            PRIORITY_LIMIT_MODE,
+            PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE,
+            PRIORITY_LAYOUT_LIMITED_FRAME_RATE,
+            PRIORITY_SYSTEM_REQUESTED_MODES,
+            PRIORITY_LOW_POWER_MODE_MODES,
+            PRIORITY_LOW_POWER_MODE_RENDER_RATE,
+            PRIORITY_FLICKER_REFRESH_RATE_SWITCH,
+            PRIORITY_SKIN_TEMPERATURE,
+            PRIORITY_PROXIMITY,
+            PRIORITY_UDFPS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Priority {}
+
     // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
     // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
-
-    int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
-    int MAX_PRIORITY = PRIORITY_UDFPS;
+    @Priority int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
+    @Priority int MAX_PRIORITY = PRIORITY_UDFPS;
 
     // The cutoff for the app request refresh rate range. Votes with priorities lower than this
     // value will not be considered when constructing the app request refresh rate range.
-    int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
+    @Priority int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
             PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;
 
     /**
@@ -205,13 +233,6 @@
         return new SupportedModesVote(modeIds);
     }
 
-    static Vote forSupportedRefreshRatesAndDisableSwitching(
-            List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) {
-        return new CombinedVote(
-                List.of(forDisableRefreshRateSwitching(),
-                        new SupportedRefreshRatesVote(supportedRefreshRates)));
-    }
-
     static String priorityToString(int priority) {
         switch (priority) {
             case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index 6becf1c..d41ef65 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -79,12 +79,12 @@
     }
 
     /** updates vote storage for all displays */
-    void updateGlobalVote(int priority, @Nullable Vote vote) {
+    void updateGlobalVote(@Vote.Priority int priority, @Nullable Vote vote) {
         updateVote(GLOBAL_ID, priority, vote);
     }
 
     /** updates vote storage */
-    void updateVote(int displayId, int priority, @Nullable Vote vote) {
+    void updateVote(int displayId, @Vote.Priority int priority, @Nullable Vote vote) {
         if (mLoggingEnabled) {
             Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
                     + ", priority=" + Vote.priorityToString(priority)
@@ -126,7 +126,7 @@
     }
 
     /** removes all votes with certain priority from vote storage */
-    void removeAllVotesForPriority(int priority) {
+    void removeAllVotesForPriority(@Vote.Priority int priority) {
         if (mLoggingEnabled) {
             Slog.i(TAG, "removeAllVotesForPriority(priority="
                     + Vote.priorityToString(priority) + ")");
diff --git a/services/core/java/com/android/server/dreams/OWNERS b/services/core/java/com/android/server/dreams/OWNERS
index 7302f6e..b9286f8 100644
--- a/services/core/java/com/android/server/dreams/OWNERS
+++ b/services/core/java/com/android/server/dreams/OWNERS
@@ -1,4 +1,3 @@
-brycelee@google.com
-dsandler@android.com
-michaelwr@google.com
-roosa@google.com
+# Bug component: 66910
+include /core/java/android/service/dreams/OWNERS
+
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
old mode 100755
new mode 100644
diff --git a/services/core/java/com/android/server/hdmi/TEST_MAPPING b/services/core/java/com/android/server/hdmi/TEST_MAPPING
index 7245ec4..c0fa121 100644
--- a/services/core/java/com/android/server/hdmi/TEST_MAPPING
+++ b/services/core/java/com/android/server/hdmi/TEST_MAPPING
@@ -30,5 +30,25 @@
         }
       ]
     }
+  ],
+  // Postsubmit tests for TV devices
+  "tv-postsubmit": [
+    {
+      "name": "HdmiCecTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.hardware.hdmi"
+        }
+      ],
+      "file_patterns": [
+        "(/|^)DeviceFeature[^/]*", "(/|^)Hdmi[^/]*"
+      ]
+    }
   ]
 }
diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
index 8a3a56c..fd3a92e 100644
--- a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
+++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java
@@ -212,6 +212,16 @@
         }
     }
 
+    public void setChargingPolicy(int policy) throws RemoteException {
+        IHealth service = mLastService.get();
+        if (service == null) return;
+        try {
+            service.setChargingPolicy(policy);
+        } catch (UnsupportedOperationException | ServiceSpecificException ex) {
+            return;
+        }
+    }
+
     private static void traceBegin(String name) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
     }
diff --git a/services/core/java/com/android/server/health/OWNERS b/services/core/java/com/android/server/health/OWNERS
index 81522fc..44ab7f7 100644
--- a/services/core/java/com/android/server/health/OWNERS
+++ b/services/core/java/com/android/server/health/OWNERS
@@ -1 +1 @@
-file:platform/hardware/interfaces:/health/aidl/OWNERS
+file:platform/hardware/interfaces:/health/OWNERS
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 48cccd5..e5dbce9 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3348,6 +3348,14 @@
         mPointerIconCache.setUseLargePointerIcons(useLargeIcons);
     }
 
+    void setPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fillStyle) {
+        mPointerIconCache.setPointerFillStyle(fillStyle);
+    }
+
+    void setPointerScale(float scale) {
+        mPointerIconCache.setPointerScale(scale);
+    }
+
     interface KeyboardBacklightControllerInterface {
         default void incrementKeyboardBacklight(int deviceId) {}
         default void decrementKeyboardBacklight(int deviceId) {}
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index a1341b7..593b091 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -16,6 +16,10 @@
 
 package com.android.server.input;
 
+import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
+import static android.view.flags.Flags.enableVectorCursorA11ySettings;
+
 import static com.android.input.flags.Flags.rateLimitUserActivityPokeInDispatcher;
 
 import android.content.BroadcastReceiver;
@@ -96,7 +100,11 @@
                 Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_STICKY_KEYS),
                         (reason) -> updateAccessibilityStickyKeys()),
                 Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED),
-                        (reason) -> updateStylusPointerIconEnabled()));
+                        (reason) -> updateStylusPointerIconEnabled()),
+                Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE),
+                        (reason) -> updatePointerFillStyleFromSettings()),
+                Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE),
+                        (reason) -> updatePointerScaleFromSettings()));
     }
 
     /**
@@ -261,4 +269,25 @@
         mNative.setStylusPointerIconEnabled(
                 InputSettings.isStylusPointerIconEnabled(mContext, true /* forceReloadSetting */));
     }
+
+    private void updatePointerFillStyleFromSettings() {
+        if (!enableVectorCursorA11ySettings()) {
+            return;
+        }
+        final int pointerFillStyle = Settings.System.getIntForUser(
+                mContext.getContentResolver(), Settings.System.POINTER_FILL_STYLE,
+                POINTER_ICON_VECTOR_STYLE_FILL_BLACK,
+                UserHandle.USER_CURRENT);
+        mService.setPointerFillStyle(pointerFillStyle);
+    }
+
+    private void updatePointerScaleFromSettings() {
+        if (!enableVectorCursorA11ySettings()) {
+            return;
+        }
+        final float pointerScale = Settings.System.getFloatForUser(mContext.getContentResolver(),
+                Settings.System.POINTER_SCALE, DEFAULT_POINTER_SCALE,
+                UserHandle.USER_CURRENT);
+        mService.setPointerScale(pointerScale);
+    }
 }
diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java
index 233b865..44622d8 100644
--- a/services/core/java/com/android/server/input/PointerIconCache.java
+++ b/services/core/java/com/android/server/input/PointerIconCache.java
@@ -16,13 +16,18 @@
 
 package com.android.server.input;
 
+import static android.view.PointerIcon.DEFAULT_POINTER_SCALE;
+import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
+
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.res.Resources;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.PointerIcon;
@@ -56,6 +61,11 @@
     private final SparseArray<Context> mDisplayContexts = new SparseArray<>();
     @GuardedBy("mLoadedPointerIconsByDisplayAndType")
     private final SparseIntArray mDisplayDensities = new SparseIntArray();
+    @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+    private @PointerIcon.PointerIconVectorStyleFill int mPointerIconFillStyle =
+            POINTER_ICON_VECTOR_STYLE_FILL_BLACK;
+    @GuardedBy("mLoadedPointerIconsByDisplayAndType")
+    private float mPointerIconScale = DEFAULT_POINTER_SCALE;
 
     private final DisplayManager.DisplayListener mDisplayListener =
             new DisplayManager.DisplayListener() {
@@ -105,6 +115,16 @@
         mUiThreadHandler.post(() -> handleSetUseLargePointerIcons(useLargeIcons));
     }
 
+    /** Set the fill style for vector pointer icons. */
+    public void setPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fillStyle) {
+        mUiThreadHandler.post(() -> handleSetPointerFillStyle(fillStyle));
+    }
+
+    /** Set the scale for vector pointer icons. */
+    public void setPointerScale(float scale) {
+        mUiThreadHandler.post(() -> handleSetPointerScale(scale));
+    }
+
     /**
      * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if
      * it isn't already cached.
@@ -119,8 +139,13 @@
             }
             PointerIcon icon = iconsByType.get(type);
             if (icon == null) {
-                icon = PointerIcon.getLoadedSystemIcon(getContextForDisplayLocked(displayId), type,
-                        mUseLargePointerIcons);
+                Context context = getContextForDisplayLocked(displayId);
+                Resources.Theme theme = context.getResources().newTheme();
+                theme.setTo(context.getTheme());
+                theme.applyStyle(PointerIcon.vectorFillStyleToResource(mPointerIconFillStyle),
+                        /* force= */ true);
+                icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme),
+                        type, mUseLargePointerIcons, mPointerIconScale);
                 iconsByType.put(type, icon);
             }
             return Objects.requireNonNull(icon);
@@ -185,6 +210,32 @@
         mNative.reloadPointerIcons();
     }
 
+    @android.annotation.UiThread
+    private void handleSetPointerFillStyle(@PointerIcon.PointerIconVectorStyleFill int fillStyle) {
+        synchronized (mLoadedPointerIconsByDisplayAndType) {
+            if (mPointerIconFillStyle == fillStyle) {
+                return;
+            }
+            mPointerIconFillStyle = fillStyle;
+            // Clear all cached icons on all displays.
+            mLoadedPointerIconsByDisplayAndType.clear();
+        }
+        mNative.reloadPointerIcons();
+    }
+
+    @android.annotation.UiThread
+    private void handleSetPointerScale(float scale) {
+        synchronized (mLoadedPointerIconsByDisplayAndType) {
+            if (mPointerIconScale == scale) {
+                return;
+            }
+            mPointerIconScale = scale;
+            // Clear all cached icons on all displays.
+            mLoadedPointerIconsByDisplayAndType.clear();
+        }
+        mNative.reloadPointerIcons();
+    }
+
     // Updates the cached display density for the given displayId, and returns true if
     // the cached density changed.
     @GuardedBy("mLoadedPointerIconsByDisplayAndType")
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index 2e44b6d..7d48527 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -32,6 +32,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.util.EventLog;
@@ -137,15 +138,17 @@
     @GuardedBy("ImfLock.class")
     @Override
     public void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
-            @ImeVisibilityStateComputer.VisibilityState int state) {
+            @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {
         applyImeVisibility(windowToken, statsToken, state,
-                SoftInputShowHideReason.NOT_SET /* ignore reason */);
+                SoftInputShowHideReason.NOT_SET /* ignore reason */, userId);
     }
 
     @GuardedBy("ImfLock.class")
     void applyImeVisibility(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
             @ImeVisibilityStateComputer.VisibilityState int state,
-            @SoftInputShowHideReason int reason) {
+            @SoftInputShowHideReason int reason, @UserIdInt int userId) {
+        final var bindingController = mService.getInputMethodBindingController(userId);
+        final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
         switch (state) {
             case STATE_SHOW_IME:
                 if (!Flags.refactorInsetsController()) {
@@ -165,8 +168,7 @@
                         // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
                         // Send it to window manager to hide IME from the actual IME control target
                         // of the target display.
-                        mWindowManagerInternal.hideIme(windowToken,
-                                mService.getDisplayIdToShowImeLocked(), statsToken);
+                        mWindowManagerInternal.hideIme(windowToken, displayIdToShowIme, statsToken);
                     } else {
                         ImeTracker.forLogging().onFailed(statsToken,
                                 ImeTracker.PHASE_SERVER_APPLY_IME_VISIBILITY);
@@ -201,10 +203,10 @@
                 }
                 break;
             case STATE_SHOW_IME_SNAPSHOT:
-                showImeScreenshot(windowToken, mService.getDisplayIdToShowImeLocked());
+                showImeScreenshot(windowToken, displayIdToShowIme);
                 break;
             case STATE_REMOVE_IME_SNAPSHOT:
-                removeImeScreenshot(mService.getDisplayIdToShowImeLocked());
+                removeImeScreenshot(displayIdToShowIme);
                 break;
             default:
                 throw new IllegalArgumentException("Invalid IME visibility state: " + state);
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index fff0e6e..56fa8c9 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -28,6 +28,7 @@
 import android.view.inputmethod.ImeTracker;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.inputmethod.IImeTracker;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
@@ -174,10 +175,18 @@
 
     @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
     @Override
-    public void finishTrackingPendingImeVisibilityRequests() {
+    public void finishTrackingPendingImeVisibilityRequests(
+            @NonNull AndroidFuture completionSignal /* T=Void */) {
         super.finishTrackingPendingImeVisibilityRequests_enforcePermission();
-        synchronized (mLock) {
-            mHistory.mLiveEntries.clear();
+        @SuppressWarnings("unchecked")
+        final AndroidFuture<Void> typedCompletionSignal = completionSignal;
+        try {
+            synchronized (mLock) {
+                mHistory.mLiveEntries.clear();
+            }
+            typedCompletionSignal.complete(null);
+        } catch (Throwable e) {
+            typedCompletionSignal.completeExceptionally(e);
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index 9f2b84d..a5f9b7a 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -17,6 +17,7 @@
 package com.android.server.inputmethod;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.view.inputmethod.ImeTracker;
@@ -63,7 +64,7 @@
      * @param state       The new IME visibility state for the applier to handle
      */
     default void applyImeVisibility(IBinder windowToken, @NonNull ImeTracker.Token statsToken,
-            @ImeVisibilityStateComputer.VisibilityState int state) {}
+            @ImeVisibilityStateComputer.VisibilityState int state, @UserIdInt int userId) {}
 
     /**
      * Updates the IME Z-ordering relative to the given window.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 8191ee1..60d647d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -17,7 +17,9 @@
 package com.android.server.inputmethod;
 
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
+import static android.content.Context.DEVICE_ID_DEFAULT;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,6 +46,7 @@
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -82,12 +85,17 @@
     @GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
     @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
     @GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken;
-    @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = Display.INVALID_DISPLAY;
+    @GuardedBy("ImfLock.class") @Nullable private InputMethodSubtype mCurrentSubtype;
+    @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = INVALID_DISPLAY;
     @GuardedBy("ImfLock.class") private int mCurSeq;
     @GuardedBy("ImfLock.class") private boolean mVisibleBound;
     @GuardedBy("ImfLock.class") private boolean mSupportsStylusHw;
     @GuardedBy("ImfLock.class") private boolean mSupportsConnectionlessStylusHw;
 
+    /** The display id for which the latest startInput was called. */
+    @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY;
+    @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
+
     @Nullable private CountDownLatch mLatchForTesting;
 
     /**
@@ -189,6 +197,18 @@
     }
 
     /**
+     * Returns {@link InputMethodInfo} that is queried from {@link #getSelectedMethodId()}.
+     *
+     * @return {@link InputMethodInfo} whose IME ID is the same as {@link #getSelectedMethodId()}.
+     *         {@code null} otherwise
+     */
+    @GuardedBy("ImfLock.class")
+    @Nullable
+    InputMethodInfo getSelectedMethod() {
+        return InputMethodSettingsRepository.get(mUserId).getMethodMap().get(mSelectedMethodId);
+    }
+
+    /**
      * The token we have made for the currently active input method, to
      * identify it in the future.
      */
@@ -199,6 +219,28 @@
     }
 
     /**
+     * The current {@link InputMethodSubtype} of the current input method.
+     *
+     * @return the current {@link InputMethodSubtype} of the current input method. {@code null}
+     *         means that there is no {@link InputMethodSubtype} currently selected
+     */
+    @GuardedBy("ImfLock.class")
+    @Nullable
+    InputMethodSubtype getCurrentSubtype() {
+        return mCurrentSubtype;
+    }
+
+    /**
+     * Sets the current {@link InputMethodSubtype} of the current input method.
+     *
+     * @param currentSubtype the current {@link InputMethodSubtype} of the current input method
+     */
+    @GuardedBy("ImfLock.class")
+    void setCurrentSubtype(@Nullable InputMethodSubtype currentSubtype) {
+        mCurrentSubtype = currentSubtype;
+    }
+
+    /**
      * Returns the displayId associated with {@link #getCurToken()}.
      *
      * @return the displayId associated with {@link #getCurToken()}. {@link Display#INVALID_DISPLAY}
@@ -455,7 +497,7 @@
         mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */,
                 false /* animateExit */, mCurTokenDisplayId);
         mCurToken = null;
-        mCurTokenDisplayId = Display.INVALID_DISPLAY;
+        mCurTokenDisplayId = INVALID_DISPLAY;
     }
 
     @GuardedBy("ImfLock.class")
@@ -478,16 +520,15 @@
             mCurId = info.getId();
             mLastBindTime = SystemClock.uptimeMillis();
 
-            final int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
             mCurToken = new Binder();
-            mCurTokenDisplayId = displayIdToShowIme;
+            mCurTokenDisplayId = mDisplayIdToShowIme;
             if (DEBUG) {
                 Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
-                        + displayIdToShowIme);
+                        + mDisplayIdToShowIme);
             }
             mWindowManagerInternal.addWindowToken(mCurToken,
                     WindowManager.LayoutParams.TYPE_INPUT_METHOD,
-                    displayIdToShowIme, null /* options */);
+                    mDisplayIdToShowIme, null /* options */);
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
                     null, null, null, mCurId, mCurSeq, false);
@@ -596,4 +637,24 @@
             unbindVisibleConnection();
         }
     }
+
+    @GuardedBy("ImfLock.class")
+    void setDisplayIdToShowIme(int displayId) {
+        mDisplayIdToShowIme = displayId;
+    }
+
+    @GuardedBy("ImfLock.class")
+    int getDisplayIdToShowIme() {
+        return mDisplayIdToShowIme;
+    }
+
+    @GuardedBy("ImfLock.class")
+    void setDeviceIdToShowIme(int deviceId) {
+        mDeviceIdToShowIme = deviceId;
+    }
+
+    @GuardedBy("ImfLock.class")
+    int getDeviceIdToShowIme() {
+        return mDeviceIdToShowIme;
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fb5f5ae..3f296ca 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -227,6 +227,16 @@
     }
 
     /**
+     * Indicates that the annotated field is shared by all the users.
+     *
+     * <p>See b/305849394 for details.</p>
+     */
+    @Retention(SOURCE)
+    @Target({ElementType.FIELD})
+    private @interface SharedByAllUsersField {
+    }
+
+    /**
      * Indicates that the annotated field is not yet ready for concurrent multi-user support.
      *
      * <p>See b/305849394 for details.</p>
@@ -260,6 +270,10 @@
     private static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
 
     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
+
+    private static final int INVALID_SUBTYPE_HASHCODE =
+            InputMethodSettings.INVALID_SUBTYPE_HASHCODE;
+
     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
     private static final String HANDLER_THREAD_NAME = "android.imms";
     private static final String PACKAGE_MONITOR_THREAD_NAME = "android.imms2";
@@ -272,6 +286,7 @@
      * {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE SOFT_INPUT_STATE_ALWAYS_VISIBLE}
      * starting from {@link android.os.Build.VERSION_CODES#P}.
      */
+    @SharedByAllUsersField
     private final boolean mPreventImeStartupUnlessTextEditor;
 
     /**
@@ -279,6 +294,7 @@
      * from the IME startup avoidance behavior that is enabled by
      * {@link #mPreventImeStartupUnlessTextEditor}.
      */
+    @SharedByAllUsersField
     @NonNull
     private final String[] mNonPreemptibleInputMethods;
 
@@ -286,6 +302,7 @@
      * See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be
      * {@code true}.
      */
+    @SharedByAllUsersField
     private final boolean mExperimentalConcurrentMultiUserModeEnabled;
 
     /**
@@ -309,7 +326,7 @@
     private final Handler mHandler;
 
     @NonNull
-    private final Handler mPackageMonitorHandler;
+    private final Handler mIoHandler;
 
     @MultiUserUnawareField
     @UserIdInt
@@ -327,6 +344,7 @@
     final PackageManagerInternal mPackageManagerInternal;
     final InputManagerInternal mInputManagerInternal;
     final ImePlatformCompatUtils mImePlatformCompatUtils;
+    @SharedByAllUsersField
     final InputMethodDeviceConfigs mInputMethodDeviceConfigs;
 
     private final UserManagerInternal mUserManagerInternal;
@@ -339,6 +357,7 @@
     private final ImeVisibilityStateComputer mVisibilityStateComputer;
 
     @GuardedBy("ImfLock.class")
+    @SharedByAllUsersField
     @NonNull
     private final DefaultImeVisibilityApplier mVisibilityApplier;
 
@@ -355,7 +374,7 @@
 
     // Mapping from deviceId to the device-specific imeId for that device.
     @GuardedBy("ImfLock.class")
-    @MultiUserUnawareField
+    @SharedByAllUsersField
     private final SparseArray<String> mVirtualDeviceMethodMap = new SparseArray<>();
 
     // TODO: Instantiate mSwitchingController for each user.
@@ -367,36 +386,15 @@
     @MultiUserUnawareField
     private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController;
 
-    /**
-     * Tracks how many times {@link #mSettings} was updated.
-     */
-    @GuardedBy("ImfLock.class")
-    private int mMethodMapUpdateCount = 0;
-
-    /**
-     * The display id for which the latest startInput was called.
-     */
-    @GuardedBy("ImfLock.class")
-    int getDisplayIdToShowImeLocked() {
-        return mDisplayIdToShowIme;
-    }
-
-    @GuardedBy("ImfLock.class")
-    @MultiUserUnawareField
-    private int mDisplayIdToShowIme = INVALID_DISPLAY;
-
-    @GuardedBy("ImfLock.class")
-    @MultiUserUnawareField
-    private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
-
     @Nullable
     private StatusBarManagerInternal mStatusBarManagerInternal;
+    @SharedByAllUsersField
     private boolean mShowOngoingImeSwitcherForPhones;
     @GuardedBy("ImfLock.class")
     @MultiUserUnawareField
     private final HandwritingModeController mHwController;
     @GuardedBy("ImfLock.class")
-    @MultiUserUnawareField
+    @SharedByAllUsersField
     private IntArray mStylusIds;
 
     @GuardedBy("ImfLock.class")
@@ -475,6 +473,7 @@
     /**
      * Manages the IME clients.
      */
+    @SharedByAllUsersField
     private final ClientController mClientController;
 
     /**
@@ -486,14 +485,22 @@
     /**
      * Set once the system is ready to run third party code.
      */
+    @SharedByAllUsersField
     boolean mSystemReady;
 
     @GuardedBy("ImfLock.class")
     @NonNull
-    InputMethodBindingController getInputMethodBindingController(@UserIdInt int userId) {
-        return mUserDataRepository.getOrCreate(userId).mBindingController;
+    UserDataRepository.UserData getUserData(@UserIdInt int userId) {
+        return mUserDataRepository.getOrCreate(userId);
     }
 
+    @GuardedBy("ImfLock.class")
+    @NonNull
+    InputMethodBindingController getInputMethodBindingController(@UserIdInt int userId) {
+        return getUserData(userId).mBindingController;
+    }
+
+
     /**
      * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
      * This is to be synchronized with the secure settings keyed with
@@ -522,6 +529,7 @@
     /**
      * The client that is currently bound to an input method.
      */
+    @MultiUserUnawareField
     @Nullable
     private ClientState mCurClient;
 
@@ -561,18 +569,13 @@
     EditorInfo mCurEditorInfo;
 
     /**
-     * The current subtype of the current input method.
-     */
-    @MultiUserUnawareField
-    private InputMethodSubtype mCurrentSubtype;
-
-    /**
      * Map of window perceptible states indexed by their associated window tokens.
      *
      * The value {@code true} indicates that IME has not been mostly hidden via
      * {@link android.view.InsetsController} for the given window.
      */
     @GuardedBy("ImfLock.class")
+    @SharedByAllUsersField
     private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>();
 
     /**
@@ -677,28 +680,36 @@
     @MultiUserUnawareField
     int mImeWindowVis;
 
+    @SharedByAllUsersField
     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
+
+    @SharedByAllUsersField
     private final String mSlotIme;
 
     /**
      * Registered {@link InputMethodListListener}.
      * This variable can be accessed from both of MainThread and BinderThread.
      */
+    @SharedByAllUsersField
     private final CopyOnWriteArrayList<InputMethodListListener> mInputMethodListListeners =
             new CopyOnWriteArrayList<>();
 
     @GuardedBy("ImfLock.class")
+    @SharedByAllUsersField
     private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
 
     @GuardedBy("ImfLock.class")
+    @SharedByAllUsersField
     @NonNull
     private final StartInputHistory mStartInputHistory = new StartInputHistory();
 
     @GuardedBy("ImfLock.class")
+    @SharedByAllUsersField
     @NonNull
     private final SoftInputShowHideHistory mSoftInputShowHideHistory =
             new SoftInputShowHideHistory();
 
+    @SharedByAllUsersField
     @NonNull
     private final ImeTrackerService mImeTrackerService;
 
@@ -1038,7 +1049,7 @@
                     // one now available?
                     changed = chooseNewDefaultIMELocked();
                 } else if (!changed && isPackageModified(curIm.getPackageName())) {
-                    // Even if the current input method is still available, mCurrentSubtype could
+                    // Even if the current input method is still available, current subtype could
                     // be obsolete when the package is modified in practice.
                     changed = true;
                 }
@@ -1162,7 +1173,7 @@
             final int userId = user.getUserIdentifier();
             SecureSettingsWrapper.onUserStarting(userId);
             synchronized (ImfLock.class) {
-                mService.mUserDataRepository.getOrCreate(userId);
+                mService.getUserData(userId);
                 if (mService.mExperimentalConcurrentMultiUserModeEnabled) {
                     if (mService.mCurrentUserId != userId && mService.mSystemReady) {
                         mService.experimentalInitializeVisibleBackgroundUserLocked(userId);
@@ -1224,7 +1235,7 @@
             Context context,
             boolean experimentalConcurrentMultiUserModeEnabled,
             @Nullable ServiceThread serviceThreadForTesting,
-            @Nullable ServiceThread packageMonitorThreadForTesting,
+            @Nullable ServiceThread ioThreadForTesting,
             @Nullable IntFunction<InputMethodBindingController> bindingControllerForTesting) {
         synchronized (ImfLock.class) {
             mExperimentalConcurrentMultiUserModeEnabled =
@@ -1245,15 +1256,15 @@
             thread.start();
             mHandler = Handler.createAsync(thread.getLooper(), this);
             {
-                final ServiceThread packageMonitorThread =
-                        packageMonitorThreadForTesting != null
-                                ? packageMonitorThreadForTesting
+                final ServiceThread ioThread =
+                        ioThreadForTesting != null
+                                ? ioThreadForTesting
                                 : new ServiceThread(
                                         PACKAGE_MONITOR_THREAD_NAME,
                                         Process.THREAD_PRIORITY_FOREGROUND,
                                         true /* allowIo */);
-                packageMonitorThread.start();
-                mPackageMonitorHandler = Handler.createAsync(packageMonitorThread.getLooper());
+                ioThread.start();
+                mIoHandler = Handler.createAsync(ioThread.getLooper());
             }
             SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
             mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
@@ -1284,7 +1295,7 @@
                     bindingControllerForTesting != null ? bindingControllerForTesting
                             : bindingControllerFactory);
             for (int id : mUserManagerInternal.getUserIds()) {
-                mUserDataRepository.getOrCreate(id);
+                getUserData(id);
             }
 
             final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
@@ -1524,7 +1535,7 @@
                     }
                 }, "Lazily initialize IMMS#mImeDrawsImeNavBarRes");
 
-                mMyPackageMonitor.register(mContext, UserHandle.ALL, mPackageMonitorHandler);
+                mMyPackageMonitor.register(mContext, UserHandle.ALL, mIoHandler);
                 mSettingsObserver.registerContentObserverLocked(currentUserId);
 
                 final IntentFilter broadcastFilterForAllUsers = new IntentFilter();
@@ -1951,7 +1962,7 @@
             final var statsToken = createStatsTokenForFocusedClient(false /* show */,
                     SoftInputShowHideReason.UNBIND_CURRENT_METHOD);
             mVisibilityApplier.applyImeVisibility(mImeBindingState.mFocusedWindow, statsToken,
-                    STATE_HIDE_IME);
+                    STATE_HIDE_IME, mCurrentUserId);
         }
     }
 
@@ -1992,17 +2003,18 @@
     @GuardedBy("ImfLock.class")
     @NonNull
     InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
+        final int userId = mCurrentUserId;
+        final var bindingController = getInputMethodBindingController(userId);
         if (!mBoundToMethod) {
-            getCurMethodLocked().bindInput(mCurClient.mBinding);
+            bindingController.getCurMethod().bindInput(mCurClient.mBinding);
             mBoundToMethod = true;
         }
 
         final boolean restarting = !initial;
         final Binder startInputToken = new Binder();
-        final var bindingController = getInputMethodBindingController(mCurrentUserId);
         final StartInputInfo info = new StartInputInfo(mCurrentUserId,
-                getCurTokenLocked(),
-                getCurTokenDisplayIdLocked(), bindingController.getCurId(), startInputReason,
+                bindingController.getCurToken(), bindingController.getCurTokenDisplayId(),
+                bindingController.getCurId(), startInputReason,
                 restarting, UserHandle.getUserId(mCurClient.mUid),
                 mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo,
                 mImeBindingState.mFocusedWindowSoftInputMode,
@@ -2015,9 +2027,8 @@
         // same-user scenarios.
         // That said ignoring cross-user scenario will never affect IMEs that do not have
         // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
-        if (mCurrentUserId == UserHandle.getUserId(
-                mCurClient.mUid)) {
-            mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */,
+        if (userId == UserHandle.getUserId(mCurClient.mUid)) {
+            mPackageManagerInternal.grantImplicitAccess(userId, null /* intent */,
                     UserHandle.getAppId(bindingController.getCurMethodUid()),
                     mCurClient.mUid, true /* direct */);
         }
@@ -2047,7 +2058,7 @@
         }
 
         final var curId = bindingController.getCurId();
-        final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId)
+        final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(userId)
                 .getMethodMap().get(curId);
         final boolean suppressesSpellChecker =
                 curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
@@ -2122,7 +2133,8 @@
             return InputBindResult.NOT_IME_TARGET_WINDOW;
         }
         final int csDisplayId = cs.mSelfReportedDisplayId;
-        mDisplayIdToShowIme = mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId);
+        bindingController.setDisplayIdToShowIme(
+                mVisibilityStateComputer.computeImeDisplayId(winState, csDisplayId));
 
         // Potentially override the selected input method if the new display belongs to a virtual
         // device with a custom IME.
@@ -2132,7 +2144,8 @@
         if (deviceMethodId == null) {
             mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true);
         } else if (!Objects.equals(deviceMethodId, selectedMethodId)) {
-            setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme);
+            setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID,
+                    bindingController.getDeviceIdToShowIme());
             selectedMethodId = deviceMethodId;
         }
 
@@ -2193,8 +2206,9 @@
         // We expect the caller has already verified that the client is allowed to access this
         // display ID.
         final String curId = bindingController.getCurId();
+        final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
         if (curId != null && curId.equals(bindingController.getSelectedMethodId())
-                && mDisplayIdToShowIme == getCurTokenDisplayIdLocked()) {
+                && displayIdToShowIme == getCurTokenDisplayIdLocked()) {
             if (cs.mCurSession != null) {
                 // Fast case: if we are already connected to the input method,
                 // then just return it.
@@ -2244,9 +2258,12 @@
         }
 
         final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
-        final int oldDeviceId = mDeviceIdToShowIme;
-        mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme);
-        if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+        final var bindingController = getInputMethodBindingController(userId);
+        final int oldDeviceId = bindingController.getDeviceIdToShowIme();
+        final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
+        final int newDeviceId = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme);
+        bindingController.setDeviceIdToShowIme(newDeviceId);
+        if (newDeviceId == DEVICE_ID_DEFAULT) {
             if (oldDeviceId == DEVICE_ID_DEFAULT) {
                 return currentMethodId;
             }
@@ -2258,13 +2275,12 @@
             return defaultDeviceMethodId;
         }
 
-        final String deviceMethodId =
-                mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId);
+        final String deviceMethodId = mVirtualDeviceMethodMap.get(newDeviceId, currentMethodId);
         if (Objects.equals(deviceMethodId, currentMethodId)) {
             return currentMethodId;
         } else if (!settings.getMethodMap().containsKey(deviceMethodId)) {
             if (DEBUG) {
-                Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme
+                Slog.v(TAG, "Disabling IME on virtual device with id " + newDeviceId
                         + " because its custom input method is not available: " + deviceMethodId);
             }
             return null;
@@ -2279,7 +2295,7 @@
         if (DEBUG) {
             Slog.v(TAG, "Switching current input method from " + currentMethodId
                     + " to device-specific one " + deviceMethodId + " because the current display "
-                    + mDisplayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme);
+                    + displayIdToShowIme + " belongs to device with id " + newDeviceId);
         }
         return deviceMethodId;
     }
@@ -2478,8 +2494,7 @@
 
     @GuardedBy("ImfLock.class")
     void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
-        final var bindingController =
-                mUserDataRepository.getOrCreate(mCurrentUserId).mBindingController;
+        final var bindingController = getInputMethodBindingController(mCurrentUserId);
         bindingController.setSelectedMethodId(null);
 
         // Callback before clean-up binding states.
@@ -2695,25 +2710,27 @@
         final boolean canImeDrawsImeNavBar =
                 mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar;
         final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
-                InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE);
+                InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE,
+                mCurrentUserId);
         return (canImeDrawsImeNavBar ? InputMethodNavButtonFlags.IME_DRAWS_IME_NAV_BAR : 0)
                 | (shouldShowImeSwitcherWhenImeIsShown
                 ? InputMethodNavButtonFlags.SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN : 0);
     }
 
     @GuardedBy("ImfLock.class")
-    private boolean shouldShowImeSwitcherLocked(int visibility) {
+    private boolean shouldShowImeSwitcherLocked(int visibility, @UserIdInt int userId) {
         if (!mShowOngoingImeSwitcherForPhones) return false;
         // When the IME switcher dialog is shown, the IME switcher button should be hidden.
+        // TODO(b/305849394): Make mMenuController multi-user aware.
         if (mMenuController.getSwitchingDialogLocked() != null) return false;
         // When we are switching IMEs, the IME switcher button should be hidden.
-        final var bindingController = getInputMethodBindingController(mCurrentUserId);
+        final var bindingController = getInputMethodBindingController(userId);
         if (!Objects.equals(bindingController.getCurId(),
                 bindingController.getSelectedMethodId())) {
             return false;
         }
         if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
-                && mWindowManagerInternal.isKeyguardSecure(mCurrentUserId)) {
+                && mWindowManagerInternal.isKeyguardSecure(userId)) {
             return false;
         }
         if ((visibility & InputMethodService.IME_ACTIVE) == 0
@@ -2730,7 +2747,7 @@
             return false;
         }
 
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
         List<InputMethodInfo> imes = settings.getEnabledInputMethodListWithFilter(
                 InputMethodInfo::shouldShowInInputMethodPicker);
         final int numImes = imes.size();
@@ -2845,14 +2862,22 @@
     // Caution! This method is called in this class. Handle multi-user carefully
     @GuardedBy("ImfLock.class")
     private void updateSystemUiLocked(int vis, int backDisposition) {
-        if (getCurTokenLocked() == null) {
+        updateSystemUiLocked(vis, backDisposition, mCurrentUserId);
+    }
+
+    @GuardedBy("ImfLock.class")
+    private void updateSystemUiLocked(int vis, int backDisposition, @UserIdInt int userId) {
+        final var bindingController = getInputMethodBindingController(userId);
+        final var curToken = bindingController.getCurToken();
+        if (curToken == null) {
             return;
         }
+        final int curTokenDisplayId = bindingController.getCurTokenDisplayId();
         if (DEBUG) {
             Slog.d(TAG, "IME window vis: " + vis
                     + " active: " + (vis & InputMethodService.IME_ACTIVE)
                     + " inv: " + (vis & InputMethodService.IME_INVISIBLE)
-                    + " displayId: " + getCurTokenDisplayIdLocked());
+                    + " displayId: " + curTokenDisplayId);
         }
         final IBinder focusedWindowToken = mImeBindingState != null
                 ? mImeBindingState.mFocusedWindow : null;
@@ -2871,17 +2896,18 @@
             } else {
                 vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
             }
-            final var curId = getInputMethodBindingController(mCurrentUserId).getCurId();
+            final var curId = bindingController.getCurId();
+            // TODO(b/305849394): Make mMenuController multi-user aware.
             if (mMenuController.getSwitchingDialogLocked() != null
-                    || !Objects.equals(curId, getSelectedMethodIdLocked())) {
+                    || !Objects.equals(curId, bindingController.getSelectedMethodId())) {
                 // When the IME switcher dialog is shown, or we are switching IMEs,
                 // the back button should be in the default state (as if the IME is not shown).
                 backDisposition = InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
             }
-            final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
+            final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis, userId);
             if (mStatusBarManagerInternal != null) {
-                mStatusBarManagerInternal.setImeWindowStatus(getCurTokenDisplayIdLocked(),
-                        getCurTokenLocked(), vis, backDisposition, needsToShowImeSwitcher);
+                mStatusBarManagerInternal.setImeWindowStatus(curTokenDisplayId,
+                        curToken, vis, backDisposition, needsToShowImeSwitcher);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -2909,7 +2935,7 @@
      *     <li>
      *         {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated.
      *     </li>
-     *     <li>{@link #mDeviceIdToShowIme} is ignored.</li>
+     *     <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li>
      *     <li>{@link #mSwitchingController} is ignored.</li>
      *     <li>{@link #mHardwareKeyboardShortcutController} is ignored.</li>
      *     <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li>
@@ -2948,10 +2974,11 @@
 
     @GuardedBy("ImfLock.class")
     void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final int userId = mCurrentUserId;
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
         if (enabledMayChange) {
             final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext,
-                    settings.getUserId());
+                    userId);
 
             List<InputMethodInfo> enabled = settings.getEnabledInputMethodList();
             for (int i = 0; i < enabled.size(); i++) {
@@ -2978,22 +3005,22 @@
             }
         }
 
-        if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+        final var bindingController = getInputMethodBindingController(mCurrentUserId);
+        if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) {
             String ime = SecureSettingsWrapper.getString(
-                    Settings.Secure.DEFAULT_INPUT_METHOD, null, settings.getUserId());
+                    Settings.Secure.DEFAULT_INPUT_METHOD, null, userId);
             String defaultDeviceIme = SecureSettingsWrapper.getString(
-                    Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
+                    Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId);
             if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) {
                 if (DEBUG) {
                     Slog.v(TAG, "Current input method " + ime + " differs from the stored default"
-                            + " device input method for user " + settings.getUserId()
+                            + " device input method for user " + userId
                             + " - restoring " + defaultDeviceIme);
                 }
                 SecureSettingsWrapper.putString(
-                        Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme,
-                        settings.getUserId());
+                        Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme, userId);
                 SecureSettingsWrapper.putString(
-                        Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId());
+                        Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, userId);
             }
         }
 
@@ -3019,18 +3046,18 @@
         }
 
         // TODO: Instantiate mSwitchingController for each user.
-        if (settings.getUserId() == mSwitchingController.getUserId()) {
+        if (userId == mSwitchingController.getUserId()) {
             mSwitchingController.resetCircularListLocked(settings.getMethodMap());
         } else {
             mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
-                    mContext, settings.getMethodMap(), settings.getUserId());
+                    mContext, settings.getMethodMap(), userId);
         }
         // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
-        if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+        if (userId == mHardwareKeyboardShortcutController.getUserId()) {
             mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
         } else {
             mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
-                    settings.getMethodMap(), settings.getUserId());
+                    settings.getMethodMap(), userId);
         }
         sendOnNavButtonFlagsChangedLocked();
     }
@@ -3054,21 +3081,22 @@
 
     @GuardedBy("ImfLock.class")
     void setInputMethodLocked(String id, int subtypeId, int deviceId) {
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final int userId = mCurrentUserId;
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
         InputMethodInfo info = settings.getMethodMap().get(id);
         if (info == null) {
             throw getExceptionForUnknownImeId(id);
         }
 
+        final var bindingController = getInputMethodBindingController(userId);
         // See if we need to notify a subtype change within the same IME.
-        if (id.equals(getSelectedMethodIdLocked())) {
-            final int userId = settings.getUserId();
+        if (id.equals(bindingController.getSelectedMethodId())) {
             final int subtypeCount = info.getSubtypeCount();
             if (subtypeCount <= 0) {
                 notifyInputMethodSubtypeChangedLocked(userId, info, null);
                 return;
             }
-            final InputMethodSubtype oldSubtype = mCurrentSubtype;
+            final InputMethodSubtype oldSubtype = bindingController.getCurrentSubtype();
             final InputMethodSubtype newSubtype;
             if (subtypeId >= 0 && subtypeId < subtypeCount) {
                 newSubtype = info.getSubtypeAt(subtypeId);
@@ -3076,6 +3104,7 @@
                 // If subtype is null, try to find the most applicable one from
                 // getCurrentInputMethodSubtype.
                 subtypeId = NOT_A_SUBTYPE_ID;
+                // TODO(b/347083680): The method below has questionable behaviors.
                 newSubtype = getCurrentInputMethodSubtypeLocked();
                 if (newSubtype != null) {
                     for (int i = 0; i < subtypeCount; ++i) {
@@ -3098,7 +3127,8 @@
         }
 
         // Changing to a different IME.
-        if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) {
+        if (bindingController.getDeviceIdToShowIme() != DEVICE_ID_DEFAULT
+                && deviceId == DEVICE_ID_DEFAULT) {
             // This change should only be applicable to the default device but the current input
             // method is a custom one specific to a virtual device. So only update the settings
             // entry used to restore the default device input method once we want to show the IME
@@ -3118,7 +3148,7 @@
             // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked()
             // because mCurMethodId is stored as a history in
             // setSelectedInputMethodAndSubtypeLocked().
-            getInputMethodBindingController(mCurrentUserId).setSelectedMethodId(id);
+            bindingController.setSelectedMethodId(id);
 
             if (mActivityManagerInternal.isSystemReady()) {
                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
@@ -4078,7 +4108,9 @@
             if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
-            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+            final int userId = mCurrentUserId;
+            final var bindingController = getInputMethodBindingController(userId);
+            final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
             final Pair<String, String> lastIme = settings.getLastInputMethodAndSubtype();
             final InputMethodInfo lastImi;
             if (lastIme != null) {
@@ -4086,13 +4118,15 @@
             } else {
                 lastImi = null;
             }
+            final var currentSubtype = bindingController.getCurrentSubtype();
             String targetLastImiId = null;
             int subtypeId = NOT_A_SUBTYPE_ID;
             if (lastIme != null && lastImi != null) {
-                final boolean imiIdIsSame = lastImi.getId().equals(getSelectedMethodIdLocked());
+                final boolean imiIdIsSame = lastImi.getId().equals(
+                        bindingController.getSelectedMethodId());
                 final int lastSubtypeHash = Integer.parseInt(lastIme.second);
-                final int currentSubtypeHash = mCurrentSubtype == null ? NOT_A_SUBTYPE_ID
-                        : mCurrentSubtype.hashCode();
+                final int currentSubtypeHash = currentSubtype == null ? NOT_A_SUBTYPE_ID
+                        : currentSubtype.hashCode();
                 // If the last IME is the same as the current IME and the last subtype is not
                 // defined, there is no need to switch to the last IME.
                 if (!imiIdIsSame || lastSubtypeHash != currentSubtypeHash) {
@@ -4102,7 +4136,7 @@
             }
 
             if (TextUtils.isEmpty(targetLastImiId)
-                    && !InputMethodUtils.canAddToLastInputMethod(mCurrentSubtype)) {
+                    && !InputMethodUtils.canAddToLastInputMethod(currentSubtype)) {
                 // This is a safety net. If the currentSubtype can't be added to the history
                 // and the framework couldn't find the last ime, we will make the last ime be
                 // the most applicable enabled keyboard subtype of the system imes.
@@ -4110,11 +4144,11 @@
                 if (enabled != null) {
                     final int enabledCount = enabled.size();
                     final String locale;
-                    if (mCurrentSubtype != null
-                            && !TextUtils.isEmpty(mCurrentSubtype.getLocale())) {
-                        locale = mCurrentSubtype.getLocale();
+                    if (currentSubtype != null
+                            && !TextUtils.isEmpty(currentSubtype.getLocale())) {
+                        locale = currentSubtype.getLocale();
                     } else {
-                        locale = SystemLocaleWrapper.get(mCurrentUserId).get(0).toString();
+                        locale = SystemLocaleWrapper.get(userId).get(0).toString();
                     }
                     for (int i = 0; i < enabledCount; ++i) {
                         final InputMethodInfo imi = enabled.get(i);
@@ -4161,10 +4195,11 @@
 
     @GuardedBy("ImfLock.class")
     private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) {
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final int userId = mCurrentUserId;
+        final var bindingController = getInputMethodBindingController(userId);
+        final var currentImi = bindingController.getSelectedMethod();
         final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
-                onlyCurrentIme, settings.getMethodMap().get(getSelectedMethodIdLocked()),
-                mCurrentSubtype);
+                onlyCurrentIme, currentImi, bindingController.getCurrentSubtype());
         if (nextSubtype == null) {
             return false;
         }
@@ -4179,10 +4214,11 @@
             if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
-            final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+            final int userId = mCurrentUserId;
+            final var bindingController = getInputMethodBindingController(userId);
+            final var currentImi = bindingController.getSelectedMethod();
             final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
-                    false /* onlyCurrentIme */,
-                    settings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype);
+                    false /* onlyCurrentIme */, currentImi, bindingController.getCurrentSubtype());
             return nextSubtype != null;
         }
     }
@@ -4614,13 +4650,14 @@
                 }
                 return;
             }
-            if (mCurrentUserId != mSwitchingController.getUserId()) {
+            final int userId = mCurrentUserId;
+            if (userId != mSwitchingController.getUserId()) {
                 return;
             }
-            final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId)
-                    .getMethodMap().get(getSelectedMethodIdLocked());
+            final var imi = getInputMethodBindingController(userId).getSelectedMethod();
             if (imi != null) {
-                mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
+                mSwitchingController.onUserActionLocked(imi,
+                        getInputMethodBindingController(userId).getCurrentSubtype());
             }
         }
     }
@@ -4642,7 +4679,7 @@
                         windowToken);
                 mVisibilityApplier.applyImeVisibility(requestToken, statsToken,
                         setVisible ? ImeVisibilityStateComputer.STATE_SHOW_IME
-                                : ImeVisibilityStateComputer.STATE_HIDE_IME);
+                                : ImeVisibilityStateComputer.STATE_HIDE_IME, mCurrentUserId);
             }
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -5215,9 +5252,9 @@
             Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
             return;
         }
-        mMethodMapUpdateCount++;
 
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final int userId = mCurrentUserId;
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
 
         boolean reenableMinimumNonAuxSystemImes = false;
         // TODO: The following code should find better place to live.
@@ -5280,18 +5317,18 @@
         updateDefaultVoiceImeIfNeededLocked();
 
         // TODO: Instantiate mSwitchingController for each user.
-        if (settings.getUserId() == mSwitchingController.getUserId()) {
+        if (userId == mSwitchingController.getUserId()) {
             mSwitchingController.resetCircularListLocked(settings.getMethodMap());
         } else {
             mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
                     mContext, settings.getMethodMap(), mCurrentUserId);
         }
         // TODO: Instantiate mHardwareKeyboardShortcutController for each user.
-        if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) {
+        if (userId == mHardwareKeyboardShortcutController.getUserId()) {
             mHardwareKeyboardShortcutController.reset(settings.getMethodMap());
         } else {
             mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController(
-                    settings.getMethodMap(), settings.getUserId());
+                    settings.getMethodMap(), userId);
         }
 
         sendOnNavButtonFlagsChangedLocked();
@@ -5299,7 +5336,7 @@
         // Notify InputMethodListListeners of the new installed InputMethods.
         final List<InputMethodInfo> inputMethodList = settings.getMethodList();
         mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED,
-                settings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget();
+                userId, 0 /* unused */, inputMethodList).sendToTarget();
     }
 
     @GuardedBy("ImfLock.class")
@@ -5357,7 +5394,8 @@
      */
     @GuardedBy("ImfLock.class")
     private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final int userId = mCurrentUserId;
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
         if (enabled) {
             final String enabledImeIdsStr = settings.getEnabledInputMethodsStr();
             final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds(
@@ -5376,7 +5414,8 @@
             StringBuilder builder = new StringBuilder();
             if (settings.buildAndPutEnabledInputMethodsStrRemovingId(
                     builder, enabledInputMethodsList, id)) {
-                if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) {
+                final var bindingController = getInputMethodBindingController(userId);
+                if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) {
                     // Disabled input method is currently selected, switch to another one.
                     final String selId = settings.getSelectedInputMethod();
                     if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
@@ -5405,26 +5444,34 @@
     @GuardedBy("ImfLock.class")
     private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
             boolean setSubtypeOnly) {
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final int userId = mCurrentUserId;
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
+        final var bindingController = getInputMethodBindingController(userId);
         settings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(),
-                mCurrentSubtype);
+                bindingController.getCurrentSubtype());
 
         // Set Subtype here
+        final int newSubtypeHashcode;
+        final InputMethodSubtype newSubtype;
         if (imi == null || subtypeId < 0) {
-            settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
-            mCurrentSubtype = null;
+            newSubtypeHashcode = INVALID_SUBTYPE_HASHCODE;
+            newSubtype = null;
         } else {
             if (subtypeId < imi.getSubtypeCount()) {
                 InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
-                settings.putSelectedSubtype(subtype.hashCode());
-                mCurrentSubtype = subtype;
+                newSubtypeHashcode = subtype.hashCode();
+                newSubtype = subtype;
             } else {
-                settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+                // TODO(b/347093491): Probably this should be determined from the new subtype.
+                newSubtypeHashcode = INVALID_SUBTYPE_HASHCODE;
                 // If the subtype is not specified, choose the most applicable one
-                mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
+                // TODO(b/347083680): The method below has questionable behaviors.
+                newSubtype = getCurrentInputMethodSubtypeLocked();
             }
         }
-        notifyInputMethodSubtypeChangedLocked(settings.getUserId(), imi, mCurrentSubtype);
+        settings.putSelectedSubtype(newSubtypeHashcode);
+        bindingController.setCurrentSubtype(newSubtype);
+        notifyInputMethodSubtypeChangedLocked(settings.getUserId(), imi, newSubtype);
 
         if (!setSubtypeOnly) {
             // Set InputMethod here
@@ -5434,8 +5481,9 @@
 
     @GuardedBy("ImfLock.class")
     private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
-        mDeviceIdToShowIme = DEVICE_ID_DEFAULT;
-        mDisplayIdToShowIme = INVALID_DISPLAY;
+        final var bindingController = getInputMethodBindingController(mCurrentUserId);
+        bindingController.setDisplayIdToShowIme(INVALID_DISPLAY);
+        bindingController.setDeviceIdToShowIme(DEVICE_ID_DEFAULT);
 
         final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
         settings.putSelectedDefaultDeviceInputMethod(null);
@@ -5472,6 +5520,7 @@
         }
         synchronized (ImfLock.class) {
             if (mCurrentUserId == userId) {
+                // TODO(b/347083680): The method below has questionable behaviors.
                 return getCurrentInputMethodSubtypeLocked();
             }
 
@@ -5489,48 +5538,32 @@
      *
      * <p>TODO: Address code duplication between this and
      * {@link InputMethodSettings#getCurrentInputMethodSubtypeForNonCurrentUsers()}.</p>
+     *
+     * <p>Also this method has had questionable behaviors:</p>
+     * <ul>
+     *     <li>Calling this method can update {@link #mCurrentSubtype}.</li>
+     *     <li>This method may return {@link #mCurrentSubtype} as-is, even if it does not belong
+     *         to the current IME.</li>
+     * </ul>
+     * <p>TODO(b/347083680): Address above issues.</p>
      */
     @GuardedBy("ImfLock.class")
     InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
-        String selectedMethodId = getSelectedMethodIdLocked();
+        final int userId = mCurrentUserId;
+        final var selectedMethodId = getInputMethodBindingController(userId).getSelectedMethodId();
         if (selectedMethodId == null) {
             return null;
         }
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
-        final boolean subtypeIsSelected = settings.isSubtypeSelected();
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
         final InputMethodInfo imi = settings.getMethodMap().get(selectedMethodId);
         if (imi == null || imi.getSubtypeCount() == 0) {
             return null;
         }
-        if (!subtypeIsSelected || mCurrentSubtype == null
-                || !SubtypeUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) {
-            int subtypeId = settings.getSelectedInputMethodSubtypeId(selectedMethodId);
-            if (subtypeId == NOT_A_SUBTYPE_ID) {
-                // If there are no selected subtypes, the framework will try to find
-                // the most applicable subtype from explicitly or implicitly enabled
-                // subtypes.
-                List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes =
-                        settings.getEnabledInputMethodSubtypeList(imi, true);
-                // If there is only one explicitly or implicitly enabled subtype,
-                // just returns it.
-                if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) {
-                    mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0);
-                } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) {
-                    final String locale = SystemLocaleWrapper.get(settings.getUserId())
-                            .get(0).toString();
-                    mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype(
-                            explicitlyOrImplicitlyEnabledSubtypes,
-                            SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true);
-                    if (mCurrentSubtype == null) {
-                        mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype(
-                                explicitlyOrImplicitlyEnabledSubtypes, null, locale, true);
-                    }
-                }
-            } else {
-                mCurrentSubtype = SubtypeUtils.getSubtypes(imi).get(subtypeId);
-            }
-        }
-        return mCurrentSubtype;
+        final var bindingController = getInputMethodBindingController(userId);
+        final var subtype = SubtypeUtils.getCurrentInputMethodSubtype(imi, settings,
+                bindingController.getCurrentSubtype());
+        bindingController.setCurrentSubtype(subtype);
+        return subtype;
     }
 
     /**
@@ -5596,14 +5629,16 @@
 
     @GuardedBy("ImfLock.class")
     private void switchKeyboardLayoutLocked(int direction) {
-        final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
+        final int userId = mCurrentUserId;
+        final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
 
         final InputMethodInfo currentImi = settings.getMethodMap().get(getSelectedMethodIdLocked());
         if (currentImi == null) {
             return;
         }
+        final var bindingController = getInputMethodBindingController(userId);
         final InputMethodSubtypeHandle currentSubtypeHandle =
-                InputMethodSubtypeHandle.of(currentImi, mCurrentSubtype);
+                InputMethodSubtypeHandle.of(currentImi, bindingController.getCurrentSubtype());
         final InputMethodSubtypeHandle nextSubtypeHandle =
                 mHardwareKeyboardShortcutController.onSubtypeSwitch(currentSubtypeHandle,
                         direction > 0);
@@ -5898,27 +5933,36 @@
 
         synchronized (ImfLock.class) {
             final int uid = Binder.getCallingUid();
-            if (getSelectedMethodIdLocked() == null) {
+            final int imeUserId = UserHandle.getUserId(uid);
+            if (imeUserId != mCurrentUserId) {
+                // Currently concurrent multi-user is not supported here due to the remaining
+                // dependency on mCurEditorInfo and mCurClient.
+                // TODO(b/341558132): Remove this early-exit once it becomes multi-user ready.
+                Slog.i(TAG, "Ignoring createInputContentUriToken due to user ID mismatch."
+                        + " imeUserId=" + imeUserId + " mCurrentUserId=" + mCurrentUserId);
                 return null;
             }
-            if (getCurTokenLocked() != token) {
-                Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken=" + getCurTokenLocked()
-                        + " token=" + token);
+            final var bindingController = getInputMethodBindingController(imeUserId);
+            if (bindingController.getSelectedMethodId() == null) {
+                return null;
+            }
+            if (bindingController.getCurToken() != token) {
+                Slog.e(TAG, "Ignoring createInputContentUriToken mCurToken="
+                        + bindingController.getCurToken() + " token=" + token);
                 return null;
             }
             // We cannot simply distinguish a bad IME that reports an arbitrary package name from
             // an unfortunate IME whose internal state is already obsolete due to the asynchronous
             // nature of our system.  Let's compare it with our internal record.
-            final var curPackageName = mCurEditorInfo != null
-                    ? mCurEditorInfo.packageName : null;
+            // TODO(b/341558132): Use "imeUserId" to query per-user "curEditorInfo"
+            final var curPackageName = mCurEditorInfo != null ? mCurEditorInfo.packageName : null;
             if (!TextUtils.equals(curPackageName, packageName)) {
                 Slog.e(TAG, "Ignoring createInputContentUriToken mCurEditorInfo.packageName="
                         + curPackageName + " packageName=" + packageName);
                 return null;
             }
-            // This user ID can never bee spoofed.
-            final int imeUserId = UserHandle.getUserId(uid);
-            // This user ID can never bee spoofed.
+            // This user ID can never be spoofed.
+            // TODO(b/341558132): Use "imeUserId" to query per-user "curClient"
             final int appUserId = UserHandle.getUserId(mCurClient.mUid);
             // This user ID may be invalid if "contentUri" embedded an invalid user ID.
             final int contentUriOwnerUserId = ContentProvider.getUserIdFromUri(contentUri,
@@ -6040,7 +6084,7 @@
             p.println("Current Input Method Manager state:");
             final List<InputMethodInfo> methodList = settings.getMethodList();
             int numImes = methodList.size();
-            p.println("  Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
+            p.println("  Input Methods:");
             for (int i = 0; i < numImes; i++) {
                 InputMethodInfo info = methodList.get(i);
                 p.println("  InputMethod #" + i + ":");
@@ -6107,7 +6151,7 @@
             p.println("  mStylusIds=" + (mStylusIds != null
                     ? Arrays.toString(mStylusIds.toArray()) : ""));
             p.println("  mSwitchingController:");
-            mSwitchingController.dump(p);
+            mSwitchingController.dump(p, "    ");
 
             p.println("  mStartInputHistory:");
             mStartInputHistory.dump(pw, "    ");
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
index a558838..5569e1e 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
@@ -51,8 +51,23 @@
     public static final boolean DEBUG = false;
     private static final String TAG = "InputMethodSettings";
 
-    private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
-    private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID);
+    /**
+     * An integer code that represents "no subtype" when a subtype hashcode is used.
+     *
+     * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have
+     * used {@code -1} here. We cannot change this value as it's already saved into secure settings.
+     * </p>
+     */
+    static final int INVALID_SUBTYPE_HASHCODE = -1;
+    /**
+     * A string code that represents "no subtype" when a subtype hashcode is used.
+     *
+     * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have
+     * used {@code "-1"} here. We cannot change this value as it's already saved into secure
+     * settings.</p>
+     */
+    private static final String INVALID_SUBTYPE_HASHCODE_STR =
+            String.valueOf(INVALID_SUBTYPE_HASHCODE);
     private static final char INPUT_METHOD_SEPARATOR = InputMethodUtils.INPUT_METHOD_SEPARATOR;
     private static final char INPUT_METHOD_SUBTYPE_SEPARATOR =
             InputMethodUtils.INPUT_METHOD_SUBTYPE_SEPARATOR;
@@ -259,34 +274,33 @@
     }
 
     private void saveSubtypeHistory(
-            List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
+            List<Pair<String, String>> savedImes, String newImeId, String newSubtypeHashCodeStr) {
         final StringBuilder builder = new StringBuilder();
         boolean isImeAdded = false;
-        if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
+        if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeHashCodeStr)) {
             builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
-                    newSubtypeId);
+                    newSubtypeHashCodeStr);
             isImeAdded = true;
         }
         for (int i = 0; i < savedImes.size(); ++i) {
             final Pair<String, String> ime = savedImes.get(i);
             final String imeId = ime.first;
-            String subtypeId = ime.second;
-            if (TextUtils.isEmpty(subtypeId)) {
-                subtypeId = NOT_A_SUBTYPE_ID_STR;
+            String subtypeHashCodeStr = ime.second;
+            if (TextUtils.isEmpty(subtypeHashCodeStr)) {
+                subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR;
             }
             if (isImeAdded) {
                 builder.append(INPUT_METHOD_SEPARATOR);
             } else {
                 isImeAdded = true;
             }
-            builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
-                    subtypeId);
+            builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(subtypeHashCodeStr);
         }
         // Remove the last INPUT_METHOD_SEPARATOR
         putSubtypeHistoryStr(builder.toString());
     }
 
-    private void addSubtypeToHistory(String imeId, String subtypeId) {
+    private void addSubtypeToHistory(String imeId, String subtypeHashCodeStr) {
         final List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistory();
         for (int i = 0; i < subtypeHistory.size(); ++i) {
             final Pair<String, String> ime = subtypeHistory.get(i);
@@ -301,9 +315,9 @@
             }
         }
         if (DEBUG) {
-            Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId);
+            Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeHashCodeStr);
         }
-        saveSubtypeHistory(subtypeHistory, imeId, subtypeId);
+        saveSubtypeHistory(subtypeHistory, imeId, subtypeHashCodeStr);
     }
 
     private void putSubtypeHistoryStr(@NonNull String str) {
@@ -413,26 +427,26 @@
                     for (int j = 0; j < explicitlyEnabledSubtypes.size(); ++j) {
                         final String s = explicitlyEnabledSubtypes.get(j);
                         if (s.equals(subtypeHashCode)) {
-                            // If both imeId and subtypeId are enabled, return subtypeId.
+                            // If both imeId and subtype are enabled, return subtypeId.
                             try {
                                 final int hashCode = Integer.parseInt(subtypeHashCode);
-                                // Check whether the subtype id is valid or not
-                                if (SubtypeUtils.isValidSubtypeId(imi, hashCode)) {
+                                // Check whether the subtype is valid or not
+                                if (SubtypeUtils.isValidSubtypeHashCode(imi, hashCode)) {
                                     return s;
                                 } else {
-                                    return NOT_A_SUBTYPE_ID_STR;
+                                    return INVALID_SUBTYPE_HASHCODE_STR;
                                 }
                             } catch (NumberFormatException e) {
-                                return NOT_A_SUBTYPE_ID_STR;
+                                return INVALID_SUBTYPE_HASHCODE_STR;
                             }
                         }
                     }
                 }
-                // If imeId was enabled but subtypeId was disabled.
-                return NOT_A_SUBTYPE_ID_STR;
+                // If imeId was enabled but subtype was disabled.
+                return INVALID_SUBTYPE_HASHCODE_STR;
             }
         }
-        // If both imeId and subtypeId are disabled, return null
+        // If both imeId and subtype are disabled, return null
         return null;
     }
 
@@ -451,14 +465,14 @@
             String nextImsStr = inputMethodSplitter.next();
             subtypeSplitter.setString(nextImsStr);
             if (subtypeSplitter.hasNext()) {
-                String subtypeId = NOT_A_SUBTYPE_ID_STR;
+                String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR;
                 // The first element is ime id.
                 String imeId = subtypeSplitter.next();
                 while (subtypeSplitter.hasNext()) {
-                    subtypeId = subtypeSplitter.next();
+                    subtypeHashCodeStr = subtypeSplitter.next();
                     break;
                 }
-                imsList.add(new Pair<>(imeId, subtypeId));
+                imsList.add(new Pair<>(imeId, subtypeHashCodeStr));
             }
         }
         return imsList;
@@ -528,13 +542,8 @@
         return imi;
     }
 
-    boolean isSubtypeSelected() {
-        return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID;
-    }
-
     private int getSelectedInputMethodSubtypeHashCode() {
-        return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
-                NOT_A_SUBTYPE_ID);
+        return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, INVALID_SUBTYPE_HASHCODE);
     }
 
     @UserIdInt
@@ -545,7 +554,7 @@
     int getSelectedInputMethodSubtypeId(String selectedImiId) {
         final InputMethodInfo imi = mMethodMap.get(selectedImiId);
         if (imi == null) {
-            return NOT_A_SUBTYPE_ID;
+            return InputMethodUtils.NOT_A_SUBTYPE_ID;
         }
         final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
         return SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode);
@@ -553,12 +562,12 @@
 
     void saveCurrentInputMethodAndSubtypeToHistory(String curMethodId,
             InputMethodSubtype currentSubtype) {
-        String subtypeId = NOT_A_SUBTYPE_ID_STR;
+        String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR;
         if (currentSubtype != null) {
-            subtypeId = String.valueOf(currentSubtype.hashCode());
+            subtypeHashCodeStr = String.valueOf(currentSubtype.hashCode());
         }
         if (InputMethodUtils.canAddToLastInputMethod(currentSubtype)) {
-            addSubtypeToHistory(curMethodId, subtypeId);
+            addSubtypeToHistory(curMethodId, subtypeHashCodeStr);
         }
     }
 
@@ -583,7 +592,7 @@
         }
 
         final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode();
-        if (subtypeHashCode != NOT_A_SUBTYPE_ID) {
+        if (subtypeHashCode != INVALID_SUBTYPE_HASHCODE) {
             final int subtypeIndex = SubtypeUtils.getSubtypeIdFromHashCode(imi,
                     subtypeHashCode);
             if (subtypeIndex >= 0) {
@@ -645,10 +654,10 @@
 
         final IntArray validSubtypeHashCodes = new IntArray(subtypeHashCodes.length);
         for (int subtypeHashCode : subtypeHashCodes) {
-            if (subtypeHashCode == NOT_A_SUBTYPE_ID) {
-                continue;  // NOT_A_SUBTYPE_ID must not be saved
+            if (subtypeHashCode == INVALID_SUBTYPE_HASHCODE) {
+                continue;  // INVALID_SUBTYPE_HASHCODE must not be saved
             }
-            if (!SubtypeUtils.isValidSubtypeId(imi, subtypeHashCode)) {
+            if (!SubtypeUtils.isValidSubtypeHashCode(imi, subtypeHashCode)) {
                 continue;  // this subtype does not exist in InputMethodInfo.
             }
             if (validSubtypeHashCodes.indexOf(subtypeHashCode) >= 0) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java b/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
index 60b9a4c..68924b5 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettingsRepository.java
@@ -78,7 +78,7 @@
                                     userId,
                                     AdditionalSubtypeMapRepository.get(userId),
                                     DirectBootAwareness.AUTO);
-                    sPerUserMap.put(userId, settings);
+                    put(userId, settings);
                 }
             }
         });
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 23f947c..770e3b2 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -465,11 +465,11 @@
             return result;
         }
 
-        protected void dump(final Printer pw) {
-            pw.println("    mSwitchingAwareRotationList:");
-            mSwitchingAwareRotationList.dump(pw, "      ");
-            pw.println("    mSwitchingUnawareRotationList:");
-            mSwitchingUnawareRotationList.dump(pw, "      ");
+        protected void dump(@NonNull Printer pw, @NonNull String prefix) {
+            pw.println(prefix + "mSwitchingAwareRotationList:");
+            mSwitchingAwareRotationList.dump(pw, prefix + "  ");
+            pw.println(prefix + "mSwitchingUnawareRotationList:");
+            mSwitchingUnawareRotationList.dump(pw, prefix + "  ");
         }
     }
 
@@ -529,11 +529,11 @@
         return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
     }
 
-    public void dump(final Printer pw) {
+    public void dump(@NonNull Printer pw, @NonNull String prefix) {
         if (mController != null) {
-            mController.dump(pw);
+            mController.dump(pw, prefix);
         } else {
-            pw.println("    mController=null");
+            pw.println(prefix + "mController=null");
         }
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
index 3d5c8677..1b4c0d6 100644
--- a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java
@@ -16,9 +16,11 @@
 
 package com.android.server.inputmethod;
 
+import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.LocaleList;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -100,7 +102,7 @@
         return subtypes;
     }
 
-    static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) {
+    static boolean isValidSubtypeHashCode(InputMethodInfo imi, int subtypeHashCode) {
         return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID;
     }
 
@@ -289,4 +291,54 @@
         }
         return applicableSubtype;
     }
+
+    /**
+     * Returns a {@link InputMethodSubtype} available in {@code imi} based on
+     * {@link Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE}.
+     *
+     * @param imi            {@link InputMethodInfo} to find out the current
+     *                       {@link InputMethodSubtype}
+     * @param settings       {@link InputMethodSettings} to be used to find out the current
+     *                       {@link InputMethodSubtype}
+     * @param currentSubtype the current value that will be used as fallback
+     * @return {@link InputMethodSubtype} to be used as the current {@link InputMethodSubtype}
+     */
+    @AnyThread
+    @Nullable
+    static InputMethodSubtype getCurrentInputMethodSubtype(
+            @NonNull InputMethodInfo imi, @NonNull InputMethodSettings settings,
+            @Nullable InputMethodSubtype currentSubtype) {
+        final int userId = settings.getUserId();
+        final int selectedSubtypeHashCode = SecureSettingsWrapper.getInt(
+                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID, userId);
+        if (selectedSubtypeHashCode != NOT_A_SUBTYPE_ID && currentSubtype != null
+                && isValidSubtypeHashCode(imi, currentSubtype.hashCode())) {
+            return currentSubtype;
+        }
+
+        final int subtypeId = settings.getSelectedInputMethodSubtypeId(imi.getId());
+        if (subtypeId != NOT_A_SUBTYPE_ID) {
+            return imi.getSubtypeAt(subtypeId);
+        }
+
+        // If there are no selected subtypes, the framework will try to find the most applicable
+        // subtype from explicitly or implicitly enabled subtypes.
+        final List<InputMethodSubtype> subtypes = settings.getEnabledInputMethodSubtypeList(imi,
+                true);
+        if (subtypes.isEmpty()) {
+            return currentSubtype;
+        }
+        // If there is only one explicitly or implicitly enabled subtype,
+        // just returns it.
+        if (subtypes.size() == 1) {
+            return subtypes.get(0);
+        }
+        final String locale = SystemLocaleWrapper.get(userId).get(0).toString();
+        final var subtype = findLastResortApplicableSubtype(subtypes, SUBTYPE_MODE_KEYBOARD, locale,
+                true);
+        if (subtype != null) {
+            return subtype;
+        }
+        return findLastResortApplicableSubtype(subtypes, null, locale, true);
+    }
 }
diff --git a/services/core/java/com/android/server/locales/OWNERS b/services/core/java/com/android/server/locales/OWNERS
index e1e946b..7e35dac 100644
--- a/services/core/java/com/android/server/locales/OWNERS
+++ b/services/core/java/com/android/server/locales/OWNERS
@@ -1,5 +1,4 @@
 roosa@google.com
-pratyushmore@google.com
 goldmanj@google.com
 ankitavyas@google.com
 allenwtsu@google.com
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 880787e..12495bb 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -619,7 +619,18 @@
                 ServiceState state = telephonyManager.getServiceState();
                 if (state != null && state.isUsingNonTerrestrialNetwork()) {
                     networkRequestBuilder.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
-                    networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_SATELLITE);
+                    try {
+                        networkRequestBuilder.addTransportType(NetworkCapabilities
+                                .TRANSPORT_SATELLITE);
+                        networkRequestBuilder.removeCapability(NetworkCapabilities
+                                .NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED);
+                    } catch (IllegalArgumentException ignored) {
+                        // In case TRANSPORT_SATELLITE or NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED
+                        // are not recognized, meaning an old connectivity module runs on new
+                        // android in which case no network with such capabilities will be brought
+                        // up, so it's safe to ignore the exception.
+                        // TODO: Can remove the try-catch in next quarter release.
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index f572845..966be53 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -32,6 +32,7 @@
 import android.util.Slog;
 
 import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.server.biometrics.BiometricHandlerProvider;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -132,9 +133,11 @@
         mFaceResetLockoutTask = null;
     };
 
-    BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager, @NonNull Handler handler) {
+    BiometricDeferredQueue(@NonNull SyntheticPasswordManager spManager) {
         mSpManager = spManager;
-        mHandler = handler;
+
+        //Using a higher priority thread to avoid any delays and interruption of clients
+        mHandler = BiometricHandlerProvider.getInstance().getBiometricCallbackHandler();
         mPendingResetLockoutsForFingerprint = new ArrayList<>();
         mPendingResetLockoutsForFace = new ArrayList<>();
         mPendingResetLockouts = new ArrayList<>();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ae3d36a..22b33dd 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -687,7 +687,7 @@
 
         mSpManager = injector.getSyntheticPasswordManager(mStorage);
         mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mKeyStore);
-        mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
+        mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager);
 
         mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
                 mStorage);
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 09605fe..b0fa523 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -22,6 +22,7 @@
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
 import android.media.MediaRouter2;
+import android.media.MediaRouter2Utils;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.os.Bundle;
@@ -226,8 +227,23 @@
             return route2Info != null && mTargetOriginalRouteId.equals(route2Info.getOriginalId());
         }
 
-        public boolean isTargetRouteIdInList(@NonNull List<String> routeOriginalIdList) {
-            return routeOriginalIdList.stream().anyMatch(mTargetOriginalRouteId::equals);
+        /**
+         * Returns whether the given list of {@link MediaRoute2Info#getOriginalId() original ids}
+         * contains the {@link #mTargetOriginalRouteId target route id}.
+         */
+        public boolean isTargetRouteIdInRouteOriginalIdList(
+                @NonNull List<String> originalRouteIdList) {
+            return originalRouteIdList.stream().anyMatch(mTargetOriginalRouteId::equals);
+        }
+
+        /**
+         * Returns whether the given list of {@link MediaRoute2Info#getId() unique ids} contains the
+         * {@link #mTargetOriginalRouteId target route id}.
+         */
+        public boolean isTargetRouteIdInRouteUniqueIdList(@NonNull List<String> uniqueRouteIdList) {
+            return uniqueRouteIdList.stream()
+                    .map(MediaRouter2Utils::getOriginalId)
+                    .anyMatch(mTargetOriginalRouteId::equals);
         }
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 71cbcb9..3673eb0 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -21,6 +21,7 @@
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -41,6 +42,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.LongSparseArray;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -49,15 +51,14 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
-/**
- * Maintains a connection to a particular {@link MediaRoute2ProviderService}.
- */
-final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
-        implements ServiceConnection {
+/** Maintains a connection to a particular {@link MediaRoute2ProviderService}. */
+final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider {
     private static final String TAG = "MR2ProviderSvcProxy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -65,6 +66,7 @@
     private final int mUserId;
     private final Handler mHandler;
     private final boolean mIsSelfScanOnlyProvider;
+    private final ServiceConnection mServiceConnection = new ServiceConnectionImpl();
 
     // Connection state
     private boolean mRunning;
@@ -77,7 +79,16 @@
     private boolean mLastDiscoveryPreferenceIncludesThisPackage = false;
 
     @GuardedBy("mLock")
-    final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>();
+    private final List<RoutingSessionInfo> mReleasingSessions = new ArrayList<>();
+
+    // We keep pending requests for transfers and sessions creation separately because transfers
+    // don't have an associated request id and session creations don't have a session id.
+    @GuardedBy("mLock")
+    private final LongSparseArray<SessionCreationOrTransferRequest>
+            mRequestIdToSessionCreationRequest;
+
+    @GuardedBy("mLock")
+    private final Map<String, SessionCreationOrTransferRequest> mSessionOriginalIdToTransferRequest;
 
     MediaRoute2ProviderServiceProxy(
             @NonNull Context context,
@@ -87,6 +98,8 @@
             int userId) {
         super(componentName);
         mContext = Objects.requireNonNull(context, "Context must not be null.");
+        mRequestIdToSessionCreationRequest = new LongSparseArray<>();
+        mSessionOriginalIdToTransferRequest = new HashMap<>();
         mIsSelfScanOnlyProvider = isSelfScanOnlyProvider;
         mUserId = userId;
         mHandler = new Handler(looper);
@@ -109,6 +122,18 @@
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName) {
         if (mConnectionReady) {
+            if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+                synchronized (mLock) {
+                    mRequestIdToSessionCreationRequest.put(
+                            requestId,
+                            new SessionCreationOrTransferRequest(
+                                    requestId,
+                                    routeOriginalId,
+                                    transferReason,
+                                    transferInitiatorUserHandle,
+                                    transferInitiatorPackageName));
+                }
+            }
             mActiveConnection.requestCreateSession(
                     requestId, packageName, routeOriginalId, sessionHints);
             updateBinding();
@@ -118,6 +143,11 @@
     @Override
     public void releaseSession(long requestId, String sessionId) {
         if (mConnectionReady) {
+            if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+                synchronized (mLock) {
+                    mSessionOriginalIdToTransferRequest.remove(sessionId);
+                }
+            }
             mActiveConnection.releaseSession(requestId, sessionId);
             updateBinding();
         }
@@ -158,6 +188,18 @@
             String routeOriginalId,
             @RoutingSessionInfo.TransferReason int transferReason) {
         if (mConnectionReady) {
+            if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+                synchronized (mLock) {
+                    mSessionOriginalIdToTransferRequest.put(
+                            sessionOriginalId,
+                            new SessionCreationOrTransferRequest(
+                                    requestId,
+                                    routeOriginalId,
+                                    transferReason,
+                                    transferInitiatorUserHandle,
+                                    transferInitiatorPackageName));
+                }
+            }
             mActiveConnection.transferToRoute(requestId, sessionOriginalId, routeOriginalId);
         }
     }
@@ -259,9 +301,12 @@
             Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
             service.setComponent(mComponentName);
             try {
-                mBound = mContext.bindServiceAsUser(service, this,
-                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
-                        new UserHandle(mUserId));
+                mBound =
+                        mContext.bindServiceAsUser(
+                                service,
+                                mServiceConnection,
+                                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                                new UserHandle(mUserId));
                 if (!mBound && DEBUG) {
                     Slog.d(TAG, this + ": Bind failed");
                 }
@@ -281,12 +326,11 @@
 
             mBound = false;
             disconnect();
-            mContext.unbindService(this);
+            mContext.unbindService(mServiceConnection);
         }
     }
 
-    @Override
-    public void onServiceConnected(ComponentName name, IBinder service) {
+    private void onServiceConnectedInternal(IBinder service) {
         if (DEBUG) {
             Slog.d(TAG, this + ": Connected");
         }
@@ -310,16 +354,14 @@
         }
     }
 
-    @Override
-    public void onServiceDisconnected(ComponentName name) {
+    private void onServiceDisconnectedInternal() {
         if (DEBUG) {
             Slog.d(TAG, this + ": Service disconnected");
         }
         disconnect();
     }
 
-    @Override
-    public void onBindingDied(ComponentName name) {
+    private void onBindingDiedInternal(ComponentName name) {
         unbind();
         if (Flags.enablePreventionOfKeepAliveRouteProviders()) {
             Slog.w(
@@ -384,6 +426,11 @@
         String newSessionId = newSession.getId();
 
         synchronized (mLock) {
+            if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+                newSession =
+                        createSessionWithPopulatedTransferInitiationDataLocked(
+                                requestId, /* oldSessionInfo= */ null, newSession);
+            }
             if (mSessionInfos.stream()
                     .anyMatch(session -> TextUtils.equals(session.getId(), newSessionId))
                     || mReleasingSessions.stream()
@@ -397,6 +444,7 @@
         mCallback.onSessionCreated(this, requestId, newSession);
     }
 
+    @GuardedBy("mLock")
     private int findSessionByIdLocked(RoutingSessionInfo session) {
         for (int i = 0; i < mSessionInfos.size(); i++) {
             if (TextUtils.equals(mSessionInfos.get(i).getId(), session.getId())) {
@@ -417,7 +465,6 @@
             for (RoutingSessionInfo session : sessions) {
                 if (session == null) continue;
                 session = assignProviderIdForSession(session);
-
                 int sourceIndex = findSessionByIdLocked(session);
                 if (sourceIndex < 0) {
                     mSessionInfos.add(targetIndex++, session);
@@ -425,6 +472,12 @@
                 } else if (sourceIndex < targetIndex) {
                     Slog.w(TAG, "Ignoring duplicate session ID: " + session.getId());
                 } else {
+                    if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+                        RoutingSessionInfo oldSessionInfo = mSessionInfos.get(sourceIndex);
+                        session =
+                                createSessionWithPopulatedTransferInitiationDataLocked(
+                                        REQUEST_ID_NONE, oldSessionInfo, session);
+                    }
                     mSessionInfos.set(sourceIndex, session);
                     Collections.swap(mSessionInfos, sourceIndex, targetIndex++);
                     dispatchSessionUpdated(session);
@@ -432,11 +485,65 @@
             }
             for (int i = mSessionInfos.size() - 1; i >= targetIndex; i--) {
                 RoutingSessionInfo releasedSession = mSessionInfos.remove(i);
+                mSessionOriginalIdToTransferRequest.remove(releasedSession.getId());
                 dispatchSessionReleased(releasedSession);
             }
         }
     }
 
+    /**
+     * Returns a {@link RoutingSessionInfo} with transfer initiation data from the given {@code
+     * oldSessionInfo}, and any pending transfer or session creation requests.
+     */
+    @GuardedBy("mLock")
+    private RoutingSessionInfo createSessionWithPopulatedTransferInitiationDataLocked(
+            long requestId,
+            @Nullable RoutingSessionInfo oldSessionInfo,
+            @NonNull RoutingSessionInfo newSessionInfo) {
+        SessionCreationOrTransferRequest pendingRequest =
+                oldSessionInfo != null
+                        ? mSessionOriginalIdToTransferRequest.get(newSessionInfo.getOriginalId())
+                        : mRequestIdToSessionCreationRequest.get(requestId);
+        boolean pendingTargetRouteInSelectedRoutes =
+                pendingRequest != null
+                        && pendingRequest.isTargetRouteIdInRouteUniqueIdList(
+                                newSessionInfo.getSelectedRoutes());
+        boolean pendingTargetRouteInTransferableRoutes =
+                pendingRequest != null
+                        && pendingRequest.isTargetRouteIdInRouteUniqueIdList(
+                                newSessionInfo.getTransferableRoutes());
+
+        int transferReason;
+        UserHandle transferInitiatorUserHandle;
+        String transferInitiatorPackageName;
+        if (pendingTargetRouteInSelectedRoutes) { // The pending request has been satisfied.
+            transferReason = pendingRequest.mTransferReason;
+            transferInitiatorUserHandle = pendingRequest.mTransferInitiatorUserHandle;
+            transferInitiatorPackageName = pendingRequest.mTransferInitiatorPackageName;
+        } else if (oldSessionInfo != null) {
+            // No pending request, we copy the values from the old session object.
+            transferReason = oldSessionInfo.getTransferReason();
+            transferInitiatorUserHandle = oldSessionInfo.getTransferInitiatorUserHandle();
+            transferInitiatorPackageName = oldSessionInfo.getTransferInitiatorPackageName();
+        } else { // There's a new session with no associated creation request, we use defaults.
+            transferReason = RoutingSessionInfo.TRANSFER_REASON_FALLBACK;
+            transferInitiatorUserHandle = UserHandle.of(mUserId);
+            transferInitiatorPackageName = newSessionInfo.getClientPackageName();
+        }
+        if (pendingTargetRouteInSelectedRoutes || !pendingTargetRouteInTransferableRoutes) {
+            // The pending request has been satisfied, or the target route is no longer available.
+            if (oldSessionInfo != null) {
+                mSessionOriginalIdToTransferRequest.remove(newSessionInfo.getId());
+            } else if (pendingRequest != null) {
+                mRequestIdToSessionCreationRequest.remove(pendingRequest.mRequestId);
+            }
+        }
+        return new RoutingSessionInfo.Builder(newSessionInfo)
+                .setTransferInitiator(transferInitiatorUserHandle, transferInitiatorPackageName)
+                .setTransferReason(transferReason)
+                .build();
+    }
+
     private void onSessionReleased(Connection connection, RoutingSessionInfo releasedSession) {
         if (mActiveConnection != connection) {
             return;
@@ -450,6 +557,7 @@
 
         boolean found = false;
         synchronized (mLock) {
+            mSessionOriginalIdToTransferRequest.remove(releasedSession.getId());
             for (RoutingSessionInfo session : mSessionInfos) {
                 if (TextUtils.equals(session.getId(), releasedSession.getId())) {
                     mSessionInfos.remove(session);
@@ -498,6 +606,11 @@
     }
 
     private void onRequestFailed(Connection connection, long requestId, int reason) {
+        if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
+            synchronized (mLock) {
+                mRequestIdToSessionCreationRequest.remove(requestId);
+            }
+        }
         if (mActiveConnection != connection) {
             return;
         }
@@ -522,18 +635,60 @@
                 }
                 mSessionInfos.clear();
                 mReleasingSessions.clear();
+                mRequestIdToSessionCreationRequest.clear();
+                mSessionOriginalIdToTransferRequest.clear();
             }
         }
     }
 
     @Override
     protected String getDebugString() {
+        int pendingSessionCreationCount;
+        int pendingTransferCount;
+        synchronized (mLock) {
+            pendingSessionCreationCount = mRequestIdToSessionCreationRequest.size();
+            pendingTransferCount = mSessionOriginalIdToTransferRequest.size();
+        }
         return TextUtils.formatSimple(
-                "ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b)",
+                "ProviderServiceProxy - package: %s, bound: %b, connection (active:%b, ready:%b), "
+                        + "pending (session creations: %d, transfers: %d)",
                 mComponentName.getPackageName(),
                 mBound,
                 mActiveConnection != null,
-                mConnectionReady);
+                mConnectionReady,
+                pendingSessionCreationCount,
+                pendingTransferCount);
+    }
+
+    // All methods in this class are called on the main thread.
+    private final class ServiceConnectionImpl implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (Flags.enableMr2ServiceNonMainBgThread()) {
+                mHandler.post(() -> onServiceConnectedInternal(service));
+            } else {
+                onServiceConnectedInternal(service);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (Flags.enableMr2ServiceNonMainBgThread()) {
+                mHandler.post(() -> onServiceDisconnectedInternal());
+            } else {
+                onServiceDisconnectedInternal();
+            }
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            if (Flags.enableMr2ServiceNonMainBgThread()) {
+                mHandler.post(() -> onBindingDiedInternal(name));
+            } else {
+                onBindingDiedInternal(name);
+            }
+        }
     }
 
     private final class Connection implements DeathRecipient {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index c03497e..ba7d3b8 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -70,6 +70,7 @@
 import com.android.media.flags.Flags;
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -118,6 +119,7 @@
     private final UserManagerInternal mUserManagerInternal;
     private final Object mLock = new Object();
     private final AppOpsManager mAppOpsManager;
+    private final StatusBarManagerInternal mStatusBarManagerInternal;
     final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1);
     final ActivityManager mActivityManager;
     final PowerManager mPowerManager;
@@ -188,6 +190,7 @@
         mPowerManager = mContext.getSystemService(PowerManager.class);
         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
         mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+        mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
 
         IntentFilter screenOnOffIntentFilter = new IntentFilter();
         screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON);
@@ -260,6 +263,17 @@
         }
     }
 
+    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+    public boolean showMediaOutputSwitcherWithRouter2(@NonNull String packageName) {
+        UserHandle userHandle = Binder.getCallingUserHandle();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return showOutputSwitcher(packageName, userHandle);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     public void registerRouter2(@NonNull IMediaRouter2 router, @NonNull String packageName) {
         Objects.requireNonNull(router, "router must not be null");
         if (TextUtils.isEmpty(packageName)) {
@@ -778,6 +792,31 @@
         }
     }
 
+    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+    public boolean showMediaOutputSwitcherWithProxyRouter(
+            @NonNull IMediaRouter2Manager proxyRouter) {
+        Objects.requireNonNull(proxyRouter, "Proxy router must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                final IBinder binder = proxyRouter.asBinder();
+                ManagerRecord proxyRouterRecord = mAllManagerRecords.get(binder);
+
+                if (proxyRouterRecord.mTargetPackageName == null) {
+                    throw new UnsupportedOperationException(
+                            "Only proxy routers can show the Output Switcher.");
+                }
+
+                return showOutputSwitcher(
+                        proxyRouterRecord.mTargetPackageName,
+                        UserHandle.of(proxyRouterRecord.mUserRecord.mUserId));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     // End of methods that implement MediaRouter2Manager operations.
 
     // Start of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
@@ -934,6 +973,19 @@
         }
     }
 
+    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+    private boolean showOutputSwitcher(
+            @NonNull String packageName, @NonNull UserHandle userHandle) {
+        if (mActivityManager.getPackageImportance(packageName) > IMPORTANCE_FOREGROUND) {
+            Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground");
+            return false;
+        }
+        synchronized (mLock) {
+            mStatusBarManagerInternal.showMediaOutputSwitcher(packageName, userHandle);
+        }
+        return true;
+    }
+
     // End of methods that implements operations for both MediaRouter2 and MediaRouter2Manager.
 
     public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 192ac62..1188a07 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -16,7 +16,6 @@
 
 package com.android.server.media;
 
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -75,7 +74,6 @@
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
 import com.android.server.pm.UserManagerInternal;
-import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -266,32 +264,6 @@
 
     // Binder call
     @Override
-    public boolean showMediaOutputSwitcher(String packageName) {
-        int uid = Binder.getCallingUid();
-        if (!validatePackageName(uid, packageName)) {
-            throw new SecurityException("packageName must match the calling identity");
-        }
-        UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            if (mContext.getSystemService(ActivityManager.class).getPackageImportance(packageName)
-                    > IMPORTANCE_FOREGROUND) {
-                Slog.w(TAG, "showMediaOutputSwitcher only works when called from foreground");
-                return false;
-            }
-            synchronized (mLock) {
-                StatusBarManagerInternal statusBar =
-                        LocalServices.getService(StatusBarManagerInternal.class);
-                statusBar.showMediaOutputSwitcher(packageName, userHandle);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return true;
-    }
-
-    // Binder call
-    @Override
     public MediaRouterClientState getState(IMediaRouterClient client) {
         final long token = Binder.clearCallingIdentity();
         try {
@@ -443,6 +415,17 @@
     }
 
     // Binder call
+    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+    @Override
+    public boolean showMediaOutputSwitcherWithRouter2(@NonNull String packageName) {
+        int uid = Binder.getCallingUid();
+        if (!validatePackageName(uid, packageName)) {
+            throw new SecurityException("packageName must match the calling identity");
+        }
+        return mService2.showMediaOutputSwitcherWithRouter2(packageName);
+    }
+
+    // Binder call
     @Override
     public void registerRouter2(IMediaRouter2 router, String packageName) {
         final int uid = Binder.getCallingUid();
@@ -676,6 +659,13 @@
         mService2.releaseSessionWithManager(manager, requestId, sessionId);
     }
 
+    @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+    @Override
+    public boolean showMediaOutputSwitcherWithProxyRouter(
+            @NonNull IMediaRouter2Manager proxyRouter) {
+        return mService2.showMediaOutputSwitcherWithProxyRouter(proxyRouter);
+    }
+
     void restoreBluetoothA2dp() {
         try {
             boolean a2dpOn;
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
index dfb2b0a..89555a9 100644
--- a/services/core/java/com/android/server/media/MediaSession2Record.java
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -67,7 +67,6 @@
         // The lock is required to prevent `Controller2Callback` from using partially initialized
         // `MediaSession2Record.this`.
         synchronized (mLock) {
-            mUniqueId = sNextMediaSessionRecordId.getAndIncrement();
             mSessionToken = sessionToken;
             mService = service;
             mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper));
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 69f07d5..0a9109b 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -81,6 +81,8 @@
 import android.util.Slog;
 import android.view.KeyEvent;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.media.flags.Flags;
 import com.android.server.LocalServices;
 import com.android.server.uri.UriGrantsManagerInternal;
 
@@ -229,6 +231,14 @@
 
     private int mPolicies;
 
+    private final Runnable mUserEngagementTimeoutExpirationRunnable =
+            () -> {
+                synchronized (mLock) {
+                    updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+                }
+            };
+
+    @GuardedBy("mLock")
     private @UserEngagementState int mUserEngagementState = USER_DISENGAGED;
 
     @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED})
@@ -238,26 +248,26 @@
     /**
      * Indicates that the session is active and in one of the user engaged states.
      *
-     * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+     * @see #updateUserEngagedStateIfNeededLocked(boolean)
      */
     private static final int USER_PERMANENTLY_ENGAGED = 0;
 
     /**
      * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state.
      *
-     * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+     * @see #updateUserEngagedStateIfNeededLocked(boolean)
      */
     private static final int USER_TEMPORARY_ENGAGED = 1;
 
     /**
      * Indicates that the session is either not active or in one of the user disengaged states
      *
-     * @see #updateUserEngagedStateIfNeededLocked(boolean) ()
+     * @see #updateUserEngagedStateIfNeededLocked(boolean)
      */
     private static final int USER_DISENGAGED = 2;
 
     /**
-     * Indicates the duration of the temporary engaged states.
+     * Indicates the duration of the temporary engaged states, in milliseconds.
      *
      * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily
      * engaged, meaning the corresponding session is only considered in an engaged state for the
@@ -270,7 +280,7 @@
      * user-engaged state is not considered user-engaged when transitioning from a non-user engaged
      * state {@link PlaybackState#STATE_STOPPED}.
      */
-    private static final int TEMP_USER_ENGAGED_TIMEOUT = 600000;
+    private static final int TEMP_USER_ENGAGED_TIMEOUT_MS = 600000;
 
     public MediaSessionRecord(
             int ownerPid,
@@ -284,7 +294,6 @@
             Looper handlerLooper,
             int policies)
             throws RemoteException {
-        mUniqueId = sNextMediaSessionRecordId.getAndIncrement();
         mOwnerPid = ownerPid;
         mOwnerUid = ownerUid;
         mUserId = userId;
@@ -609,8 +618,7 @@
 
     @Override
     public void expireTempEngaged() {
-        mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
-        updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
+        mHandler.post(mUserEngagementTimeoutExpirationRunnable);
     }
 
     /**
@@ -1086,11 +1094,6 @@
                 }
             };
 
-    private final Runnable mHandleTempEngagedSessionTimeout =
-            () -> {
-                updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true);
-            };
-
     @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
     private static boolean componentNameExists(
             @NonNull ComponentName componentName, @NonNull Context context, int userId) {
@@ -1107,10 +1110,14 @@
         return !resolveInfos.isEmpty();
     }
 
+    @GuardedBy("mLock")
     private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) {
+        if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+            return;
+        }
         int oldUserEngagedState = mUserEngagementState;
         int newUserEngagedState;
-        if (!isActive() || mPlaybackState == null) {
+        if (!isActive() || mPlaybackState == null || mDestroyed) {
             newUserEngagedState = USER_DISENGAGED;
         } else if (isActive() && mPlaybackState.isActive()) {
             newUserEngagedState = USER_PERMANENTLY_ENGAGED;
@@ -1126,18 +1133,22 @@
             return;
         }
 
+        mUserEngagementState = newUserEngagedState;
         if (newUserEngagedState == USER_TEMPORARY_ENGAGED) {
-            mHandler.postDelayed(mHandleTempEngagedSessionTimeout, TEMP_USER_ENGAGED_TIMEOUT);
-        } else if (oldUserEngagedState == USER_TEMPORARY_ENGAGED) {
-            mHandler.removeCallbacks(mHandleTempEngagedSessionTimeout);
+            mHandler.postDelayed(
+                    mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS);
+        } else {
+            mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable);
         }
 
         boolean wasUserEngaged = oldUserEngagedState != USER_DISENGAGED;
         boolean isNowUserEngaged = newUserEngagedState != USER_DISENGAGED;
-        mUserEngagementState = newUserEngagedState;
         if (wasUserEngaged != isNowUserEngaged) {
-            mService.onSessionUserEngagementStateChange(
-                    /* mediaSessionRecord= */ this, /* isUserEngaged= */ isNowUserEngaged);
+            mHandler.post(
+                    () ->
+                            mService.onSessionUserEngagementStateChange(
+                                    /* mediaSessionRecord= */ this,
+                                    /* isUserEngaged= */ isNowUserEngaged));
         }
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
index b57b148..15f90d4 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java
@@ -34,8 +34,12 @@
  */
 public abstract class MediaSessionRecordImpl {
 
-    static final AtomicInteger sNextMediaSessionRecordId = new AtomicInteger(1);
-    int mUniqueId;
+    private static final AtomicInteger sNextMediaSessionRecordId = new AtomicInteger(1);
+    private final int mUniqueId;
+
+    protected MediaSessionRecordImpl() {
+        mUniqueId = sNextMediaSessionRecordId.getAndIncrement();
+    }
 
     /**
      * Get the info for this session.
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 3998667..1ebc856 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -197,6 +197,16 @@
     @GuardedBy("mLock")
     private final Map<Integer, Set<Notification>> mMediaNotifications = new HashMap<>();
 
+    /**
+     * Holds all {@link MediaSessionRecordImpl} which we've reported as being {@link
+     * ActivityManagerInternal#startForegroundServiceDelegate user engaged}.
+     *
+     * <p>This map simply prevents invoking {@link
+     * ActivityManagerInternal#startForegroundServiceDelegate} more than once per session.
+     */
+    @GuardedBy("mLock")
+    private final Set<MediaSessionRecordImpl> mFgsAllowedMediaSessionRecords = new HashSet<>();
+
     // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
     // It's always not null after the MediaSessionService is started.
     private FullUserRecord mCurrentFullUserRecord;
@@ -704,17 +714,31 @@
             int uid = mediaSessionRecord.getUid();
             for (Notification mediaNotification : mMediaNotifications.getOrDefault(uid, Set.of())) {
                 if (mediaSessionRecord.isLinkedToNotification(mediaNotification)) {
-                    startFgsDelegate(mediaSessionRecord.getForegroundServiceDelegationOptions());
+                    startFgsDelegateLocked(mediaSessionRecord);
                     return;
                 }
             }
         }
     }
 
-    private void startFgsDelegate(
-            ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) {
+    @GuardedBy("mLock")
+    private void startFgsDelegateLocked(MediaSessionRecordImpl mediaSessionRecord) {
+        ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
+                mediaSessionRecord.getForegroundServiceDelegationOptions();
+        if (foregroundServiceDelegationOptions == null) {
+            return; // This record doesn't support FGS. Typically a MediaSession2 record.
+        }
+        if (!mFgsAllowedMediaSessionRecords.add(mediaSessionRecord)) {
+            return; // This record is already FGS-started.
+        }
         final long token = Binder.clearCallingIdentity();
         try {
+            Log.i(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "startFgsDelegate: pkg=%s uid=%d",
+                            foregroundServiceDelegationOptions.mClientPackageName,
+                            foregroundServiceDelegationOptions.mClientUid));
             mActivityManagerInternal.startForegroundServiceDelegate(
                     foregroundServiceDelegationOptions, /* connection= */ null);
         } finally {
@@ -748,14 +772,29 @@
                 }
             }
 
-            stopFgsDelegate(foregroundServiceDelegationOptions);
+            stopFgsDelegateLocked(mediaSessionRecord);
         }
     }
 
-    private void stopFgsDelegate(
-            ForegroundServiceDelegationOptions foregroundServiceDelegationOptions) {
+    @GuardedBy("mLock")
+    private void stopFgsDelegateLocked(MediaSessionRecordImpl mediaSessionRecord) {
+        ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
+                mediaSessionRecord.getForegroundServiceDelegationOptions();
+        if (foregroundServiceDelegationOptions == null) {
+            return; // This record doesn't support FGS. Typically a MediaSession2 record.
+        }
+        if (!mFgsAllowedMediaSessionRecords.remove(mediaSessionRecord)) {
+            return; // This record is not FGS-started. No need to stop it.
+        }
+
         final long token = Binder.clearCallingIdentity();
         try {
+            Log.i(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "stopFgsDelegate: pkg=%s uid=%d",
+                            foregroundServiceDelegationOptions.mClientPackageName,
+                            foregroundServiceDelegationOptions.mClientUid));
             mActivityManagerInternal.stopForegroundServiceDelegate(
                     foregroundServiceDelegationOptions);
         } finally {
@@ -2679,6 +2718,9 @@
 
         @Override
         public void expireTempEngagedSessions() {
+            if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) {
+                return;
+            }
             synchronized (mLock) {
                 for (Set<MediaSessionRecordImpl> uidSessions :
                         mUserEngagedSessionsForFgs.values()) {
@@ -3194,11 +3236,8 @@
                 mMediaNotifications.get(uid).add(postedNotification);
                 for (MediaSessionRecordImpl mediaSessionRecord :
                         mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
-                    ForegroundServiceDelegationOptions foregroundServiceDelegationOptions =
-                            mediaSessionRecord.getForegroundServiceDelegationOptions();
-                    if (foregroundServiceDelegationOptions != null
-                            && mediaSessionRecord.isLinkedToNotification(postedNotification)) {
-                        startFgsDelegate(foregroundServiceDelegationOptions);
+                    if (mediaSessionRecord.isLinkedToNotification(postedNotification)) {
+                        startFgsDelegateLocked(mediaSessionRecord);
                         return;
                     }
                 }
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index bea71dc..19f16cc 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -113,6 +113,7 @@
         mWriter.println("       media_session list-sessions");
         mWriter.println("       media_session monitor <tag>");
         mWriter.println("       media_session volume [options]");
+        mWriter.println("       media_session expire-temp-engaged-sessions");
         mWriter.println();
         mWriter.println("media_session dispatch: dispatch a media key to the system.");
         mWriter.println("                KEY may be: play, pause, play-pause, mute, headsethook,");
@@ -121,6 +122,9 @@
         mWriter.println("media_session monitor: monitor updates to the specified session.");
         mWriter.println("                       Use the tag from list-sessions.");
         mWriter.println("media_session volume:  " + VolumeCtrl.USAGE);
+        mWriter.println("media_session expire-temp-engaged-sessions: Expires any ongoing");
+        mWriter.println("                timers for media sessions in a temporary user-engaged");
+        mWriter.println("                state.");
         mWriter.println();
     }
 
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6b409ee..8c6273c 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -443,7 +443,8 @@
                         boolean isTransferringToTheSelectedRoute =
                                 mPendingTransferRequest.isTargetRoute(selectedRoute);
                         boolean canBePotentiallyTransferred =
-                                mPendingTransferRequest.isTargetRouteIdInList(transferableRoutes);
+                                mPendingTransferRequest.isTargetRouteIdInRouteOriginalIdList(
+                                        transferableRoutes);
 
                         if (isTransferringToTheSelectedRoute) {
                             transferReason = mPendingTransferRequest.mTransferReason;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c60ac3a..d9e22c5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -28,6 +28,7 @@
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
 import static android.app.ActivityManager.isProcStateConsideredInteraction;
 import static android.app.ActivityManager.printCapabilitiesSummary;
@@ -523,6 +524,12 @@
      */
     private boolean mUseMeteredFirewallChains;
 
+    /**
+     * Whether or not sensitive process states and non-sensitive process-states have different
+     * delays before network is blocked after transitioning to background.
+     */
+    private boolean mUseDifferentDelaysForBackgroundChain;
+
     // See main javadoc for instructions on how to use these locks.
     final Object mUidRulesFirstLock = new Object();
     final Object mNetworkPoliciesSecondLock = new Object();
@@ -552,10 +559,43 @@
      * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} will lose network access.
      * The delay is meant to prevent churn due to quick process-state changes.
      * Note that there is no delay while granting network access.
+     *
+     * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is disabled.
      */
     @VisibleForTesting
     long mBackgroundRestrictionDelayMs = TimeUnit.SECONDS.toMillis(5);
 
+    /**
+     * Short delay after which a uid going into a process state having priority equal to
+     * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access.
+     *
+     * This will apply to apps that should be fine with losing network access immediately.
+     * It is only meant as a debounce to prevent churn due to quick process-state changes.
+     * Note that there is no delay while granting network access.
+     *
+     * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled.
+     */
+    @VisibleForTesting
+    long mBackgroundRestrictionShortDelayMs = TimeUnit.SECONDS.toMillis(2);
+
+    /**
+     * Long delay after which a uid going into a process state having priority equal to
+     * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access.
+     *
+     * Unlike {@link #mBackgroundRestrictionShortDelayMs}, this is meant to be applied to apps
+     * in sensitive proc-states like {@link ActivityManager#PROCESS_STATE_TOP_SLEEPING} and
+     * {@link ActivityManager#PROCESS_STATE_LAST_ACTIVITY}, where the user may switch to this app
+     * before this period and any latency in granting network access before resuming app activities
+     * may degrade experience.
+     *
+     * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled.
+     */
+    @VisibleForTesting
+    long mBackgroundRestrictionLongDelayMs = TimeUnit.SECONDS.toMillis(20);
+
+    @GuardedBy("mUidRulesFirstLock")
+    private long mNextProcessBackgroundUidsTime = Long.MAX_VALUE;
+
     /** Defined network policies. */
     @GuardedBy("mNetworkPoliciesSecondLock")
     final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>();
@@ -1007,6 +1047,7 @@
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
 
             mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
+            mUseDifferentDelaysForBackgroundChain = Flags.useDifferentDelaysForBackgroundChain();
 
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
@@ -1241,11 +1282,21 @@
                 // different chains may change.
                 return true;
             }
-            if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE)
+            if (mBackgroundNetworkRestricted) {
+                if ((previousProcState >= BACKGROUND_THRESHOLD_STATE)
                     != (newProcState >= BACKGROUND_THRESHOLD_STATE)) {
-                // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the
-                // BACKGROUND chain may change.
-                return true;
+                    // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: The network rules will
+                    // need to be re-evaluated for the background chain.
+                    return true;
+                }
+                if (mUseDifferentDelaysForBackgroundChain
+                        && newProcState >= BACKGROUND_THRESHOLD_STATE
+                        && getBackgroundTransitioningDelay(newProcState)
+                        < getBackgroundTransitioningDelay(previousProcState)) {
+                    // The old and new proc-state both are in the blocked state but the background
+                    // transition delay is reduced, so we may have to update the rules sooner.
+                    return true;
+                }
             }
             final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
                     | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
@@ -3965,8 +4016,8 @@
         // allow override without having plans defined.
         synchronized (mNetworkPoliciesSecondLock) {
             final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
-            if (overrideMask != SUBSCRIPTION_OVERRIDE_UNMETERED && plan == null
-                    || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
+            if (overrideMask != SUBSCRIPTION_OVERRIDE_UNMETERED && (plan == null
+                    || plan.getDataLimitBehavior() == SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN)) {
                 throw new IllegalStateException(
                         "Must provide valid SubscriptionPlan to enable overriding");
             }
@@ -4045,6 +4096,8 @@
                         + mBackgroundNetworkRestricted);
                 fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": "
                         + mUseMeteredFirewallChains);
+                fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": "
+                        + mUseDifferentDelaysForBackgroundChain);
 
                 fout.println();
                 fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -4188,20 +4241,34 @@
                     fout.decreaseIndent();
                 }
 
-                size = mBackgroundTransitioningUids.size();
-                if (size > 0) {
-                    final long nowUptime = SystemClock.uptimeMillis();
-                    fout.println("Uids transitioning to background:");
-                    fout.increaseIndent();
-                    for (int i = 0; i < size; i++) {
-                        fout.print("UID=");
-                        fout.print(mBackgroundTransitioningUids.keyAt(i));
-                        fout.print(", ");
-                        TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), nowUptime,
-                                fout);
+                if (mBackgroundNetworkRestricted) {
+                    fout.println();
+                    if (mUseDifferentDelaysForBackgroundChain) {
+                        fout.print("Background restrictions short delay: ");
+                        TimeUtils.formatDuration(mBackgroundRestrictionShortDelayMs, fout);
+                        fout.println();
+
+                        fout.print("Background restrictions long delay: ");
+                        TimeUtils.formatDuration(mBackgroundRestrictionLongDelayMs, fout);
                         fout.println();
                     }
-                    fout.decreaseIndent();
+
+                    size = mBackgroundTransitioningUids.size();
+                    if (size > 0) {
+                        final long nowUptime = SystemClock.uptimeMillis();
+                        fout.println("Uids transitioning to background:");
+                        fout.increaseIndent();
+                        for (int i = 0; i < size; i++) {
+                            fout.print("UID=");
+                            fout.print(mBackgroundTransitioningUids.keyAt(i));
+                            fout.print(", ");
+                            TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i),
+                                    nowUptime, fout);
+                            fout.println();
+                        }
+                        fout.decreaseIndent();
+                    }
+                    fout.println();
                 }
 
                 final SparseBooleanArray knownUids = new SparseBooleanArray();
@@ -4337,6 +4404,15 @@
                 || isProcStateAllowedNetworkWhileBackground(mUidState.get(uid));
     }
 
+    private long getBackgroundTransitioningDelay(int procState) {
+        if (mUseDifferentDelaysForBackgroundChain) {
+            return procState <= PROCESS_STATE_LAST_ACTIVITY ? mBackgroundRestrictionLongDelayMs
+                    : mBackgroundRestrictionShortDelayMs;
+        } else {
+            return mBackgroundRestrictionDelayMs;
+        }
+    }
+
     /**
      * Process state of UID changed; if needed, will trigger
      * {@link #updateRulesForDataUsageRestrictionsUL(int)} and
@@ -4387,19 +4463,41 @@
                         mBackgroundTransitioningUids.delete(uid);
                         updateRuleForBackgroundUL(uid);
                         updatePowerRestrictionRules = true;
-                    } else if (wasAllowed && !isAllowed) {
+                    } else if (!isAllowed) {
+                        final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid);
                         final long completionTimeMs = SystemClock.uptimeMillis()
-                                + mBackgroundRestrictionDelayMs;
-                        if (mBackgroundTransitioningUids.indexOfKey(uid) < 0) {
-                            // This is just a defensive check in case the upstream code ever makes
-                            // multiple calls for the same process state change.
-                            mBackgroundTransitioningUids.put(uid, completionTimeMs);
+                                + getBackgroundTransitioningDelay(procState);
+                        boolean completionTimeUpdated = false;
+                        if (wasAllowed) {
+                            // Rules need to transition from allowed to blocked after the respective
+                            // delay.
+                            if (transitionIdx < 0) {
+                                // This is just a defensive check in case the upstream code ever
+                                // makes multiple calls for the same process state change.
+                                mBackgroundTransitioningUids.put(uid, completionTimeMs);
+                                completionTimeUpdated = true;
+                            }
+                        } else if (mUseDifferentDelaysForBackgroundChain) {
+                            // wasAllowed was false, but the transition delay may have reduced.
+                            // Currently, this can happen when the uid transitions from
+                            // LAST_ACTIVITY to CACHED_ACTIVITY, for example.
+                            if (transitionIdx >= 0
+                                    && completionTimeMs < mBackgroundTransitioningUids.valueAt(
+                                    transitionIdx)) {
+                                mBackgroundTransitioningUids.setValueAt(transitionIdx,
+                                        completionTimeMs);
+                                completionTimeUpdated = true;
+                            }
                         }
-                        if (!mHandler.hasMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS)) {
-                            // Many uids may be in this "transitioning" state at the same time, so
-                            // using one message at a time to avoid congestion in the MessageQueue.
+                        if (completionTimeUpdated
+                                && completionTimeMs < mNextProcessBackgroundUidsTime) {
+                            // Many uids may be in this "transitioning" state at the same time,
+                            // so we always keep one message to process transition completion at
+                            // the earliest time.
+                            mHandler.removeMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS);
                             mHandler.sendEmptyMessageAtTime(
                                     MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs);
+                            mNextProcessBackgroundUidsTime = completionTimeMs;
                         }
                     }
                 }
@@ -5750,10 +5848,11 @@
                             updateRuleForBackgroundUL(uid);
                             updateRulesForPowerRestrictionsUL(uid, false);
                         }
-                    }
-                    if (nextCheckTime < Long.MAX_VALUE) {
-                        mHandler.sendEmptyMessageAtTime(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS,
-                                nextCheckTime);
+                        mNextProcessBackgroundUidsTime = nextCheckTime;
+                        if (nextCheckTime < Long.MAX_VALUE) {
+                            mHandler.sendEmptyMessageAtTime(
+                                    MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, nextCheckTime);
+                        }
                     }
                     return true;
                 }
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 669cdaa..bbc7c01 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -1,5 +1,6 @@
 set noparent
 file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
+per-file NetworkPolicyManagerService.java=jackyu@google.com, sarahchin@google.com
 
 jsharkey@android.com
 sudheersai@google.com
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index e986dd8..586baf0 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -17,3 +17,13 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "use_different_delays_for_background_chain"
+    namespace: "backstage_power"
+    description: "Grant longer grace periods for sensitive process-states before blocking network using the background chain"
+    bug: "323963467"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
old mode 100755
new mode 100644
index 60147ef..b436c8b
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -23,6 +23,7 @@
 import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.OP_RECEIVE_SENSITIVE_NOTIFICATIONS;
 import static android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR;
 import static android.app.Flags.lifetimeExtensionRefactor;
 import static android.app.Flags.sortSectionByTime;
@@ -224,6 +225,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
 import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
@@ -284,6 +286,7 @@
 import android.service.notification.NotificationServiceDumpProto;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
+import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeProto;
 import android.service.notification.ZenPolicy;
@@ -4534,6 +4537,34 @@
         }
 
         @Override
+        public List<String> getPackagesBypassingDnd(int userId,
+                                                    boolean includeConversationChannels) {
+            checkCallerIsSystem();
+
+            final ArraySet<String> packageNames = new ArraySet<>();
+
+            for (int user : mUm.getProfileIds(userId, false)) {
+                List<PackageInfo> pkgs = mPackageManagerClient.getInstalledPackagesAsUser(0, user);
+                for (PackageInfo pi : pkgs) {
+                    String pkg = pi.packageName;
+                    // If any NotificationChannel for this package is bypassing, the
+                    // package is considered bypassing.
+                    for (NotificationChannel channel : getNotificationChannelsBypassingDnd(pkg,
+                            pi.applicationInfo.uid).getList()) {
+                        // Skips non-demoted conversation channels.
+                        if (!includeConversationChannels
+                                && !TextUtils.isEmpty(channel.getConversationId())
+                                && !channel.isDemoted()) {
+                            continue;
+                        }
+                        packageNames.add(pkg);
+                    }
+                }
+            }
+            return new ArrayList<String>(packageNames);
+        }
+
+        @Override
         public boolean areChannelsBypassingDnd() {
             if (android.app.Flags.modesApi()) {
                 return mZenModeHelper.getConsolidatedNotificationPolicy().allowPriorityChannels()
@@ -5508,6 +5539,14 @@
         }
 
         @Override
+        public void setManualZenRuleDeviceEffects(ZenDeviceEffects effects) throws RemoteException {
+            checkCallerIsSystem();
+
+            mZenModeHelper.setManualZenRuleDeviceEffects(effects, computeZenOrigin(true),
+                    "Update manual mode non-policy settings", Binder.getCallingUid());
+        }
+
+        @Override
         public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule,
                 boolean fromUser) throws RemoteException {
             validateAutomaticZenRule(id, automaticZenRule);
@@ -11933,7 +11972,10 @@
             long token = Binder.clearCallingIdentity();
             try {
                 if (mPackageManager.checkUidPermission(RECEIVE_SENSITIVE_NOTIFICATIONS, uid)
-                        == PERMISSION_GRANTED || mPackageManagerInternal.isPlatformSigned(pkg)) {
+                        == PERMISSION_GRANTED || mPackageManagerInternal.isPlatformSigned(pkg)
+                        || mAppOps
+                        .noteOpNoThrow(OP_RECEIVE_SENSITIVE_NOTIFICATIONS, uid, pkg, null, null)
+                        == MODE_ALLOWED) {
                     return true;
                 }
 
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 86aa2d8..02b5f97 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -68,16 +68,17 @@
     public void evaluateConfig(ZenModeConfig config, ComponentName trigger,
             boolean processSubscriptions) {
         if (config == null) return;
-        if (config.manualRule != null && config.manualRule.condition != null
+        if (!android.app.Flags.modesUi() && config.manualRule != null
+                && config.manualRule.condition != null
                 && !config.manualRule.isTrueOrUnknown()) {
             if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule");
             config.manualRule = null;
         }
         final ArraySet<Uri> current = new ArraySet<>();
-        evaluateRule(config.manualRule, current, null, processSubscriptions);
+        evaluateRule(config.manualRule, current, null, processSubscriptions, true);
         for (ZenRule automaticRule : config.automaticRules.values()) {
             if (automaticRule.component != null) {
-                evaluateRule(automaticRule, current, trigger, processSubscriptions);
+                evaluateRule(automaticRule, current, trigger, processSubscriptions, false);
                 updateSnoozing(automaticRule);
             }
         }
@@ -131,7 +132,7 @@
 
     // Only valid for CPS backed rules
     private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger,
-            boolean processSubscriptions) {
+            boolean processSubscriptions, boolean isManual) {
         if (rule == null || rule.conditionId == null) return;
         if (rule.configurationActivity != null) return;
         final Uri id = rule.conditionId;
@@ -153,8 +154,10 @@
         }
         // empty rule? disable and bail early
         if (rule.component == null && rule.enabler == null) {
-            Log.w(TAG, "No component found for automatic rule: " + rule.conditionId);
-            rule.enabled = false;
+            if (!android.app.Flags.modesUi() || (android.app.Flags.modesUi() && !isManual)) {
+                Log.w(TAG, "No component found for automatic rule: " + rule.conditionId);
+                rule.enabled = false;
+            }
             return;
         }
         if (current != null) {
diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
index 418eacc..ec5d96d 100644
--- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java
+++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java
@@ -18,8 +18,8 @@
 
 import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
-import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY;
 import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_NONE;
+import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY;
 import static android.service.notification.NotificationServiceProto.RULE_TYPE_AUTOMATIC;
 import static android.service.notification.NotificationServiceProto.RULE_TYPE_MANUAL;
 import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN;
@@ -352,8 +352,10 @@
             ZenModeDiff.RuleDiff manualDiff = diff.getManualRuleDiff();
             if (manualDiff != null && manualDiff.hasDiff()) {
                 // a diff in the manual rule doesn't *necessarily* mean that it's responsible for
-                // the change -- only if it's been added or removed.
-                if (manualDiff.wasAdded() || manualDiff.wasRemoved()) {
+                // the change -- only if it's been added or removed or updated.
+                if (manualDiff.wasAdded() || manualDiff.wasRemoved()
+                        || (Flags.modesUi()
+                        && (manualDiff.becameActive() || manualDiff.becameInactive()))) {
                     return RULE_TYPE_MANUAL;
                 }
             }
@@ -391,10 +393,8 @@
             if (config == null) {
                 return rules;
             }
-
-            if (config.manualRule != null) {
-                // If the manual rule is non-null, then it's active. We make a copy and set the rule
-                // type so that the correct value gets logged.
+            if (config.isManualActive()) {
+                // We make a copy and set the rule type so that the correct value gets logged.
                 ZenRule rule = config.manualRule.copy();
                 rule.type = ACTIVE_RULE_TYPE_MANUAL;
                 rules.add(rule);
@@ -592,10 +592,10 @@
                 // This applies to both call and message senders, but not conversation senders,
                 // where they use the same enum values.
                 proto.write(DNDPolicyProto.ALLOW_CALLS_FROM,
-                        ZenAdapters.notificationPolicySendersToZenPolicyPeopleType(
+                        ZenAdapters.prioritySendersToPeopleType(
                                 mNewPolicy.allowCallsFrom()));
                 proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM,
-                        ZenAdapters.notificationPolicySendersToZenPolicyPeopleType(
+                        ZenAdapters.prioritySendersToPeopleType(
                                 mNewPolicy.allowMessagesFrom()));
                 proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM,
                         mNewPolicy.allowConversationsFrom());
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 454bd20..267291c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -24,6 +24,10 @@
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
+import static android.service.notification.Condition.SOURCE_UNKNOWN;
+import static android.service.notification.Condition.SOURCE_USER_ACTION;
+import static android.service.notification.Condition.STATE_FALSE;
+import static android.service.notification.Condition.STATE_TRUE;
 import static android.service.notification.NotificationServiceProto.ROOT_CONFIG;
 import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP;
 import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT;
@@ -216,7 +220,9 @@
         mAppOps = context.getSystemService(AppOpsManager.class);
         mNotificationManager = context.getSystemService(NotificationManager.class);
 
-        mDefaultConfig = readDefaultConfig(mContext.getResources());
+        mDefaultConfig = Flags.modesUi()
+                ? ZenModeConfig.getDefaultConfig()
+                : readDefaultConfig(mContext.getResources());
         updateDefaultConfigAutomaticRules();
         if (Flags.modesApi()) {
             updateDefaultAutomaticRulePolicies();
@@ -612,7 +618,7 @@
                 if (rule != null) {
                     Condition deactivated = new Condition(rule.conditionId,
                             mContext.getString(R.string.zen_mode_implicit_deactivated),
-                            Condition.STATE_FALSE);
+                            STATE_FALSE);
                     setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
                             deactivated, UPDATE_ORIGIN_APP, callingUid);
                 }
@@ -627,8 +633,7 @@
                     // would apply if changing the global interruption filter. We only do this
                     // for newly created rules, as existing rules have a pre-existing policy
                     // (whether initialized here or set via app or user).
-                    rule.zenPolicy = mConfig.toZenPolicy();
-
+                    rule.zenPolicy = mConfig.getZenPolicy().copy();
                     newConfig.automaticRules.put(rule.id, rule);
                 }
                 // If the user has changed the rule's *zenMode*, then don't let app overwrite it.
@@ -639,7 +644,7 @@
                 rule.snoozing = false;
                 rule.condition = new Condition(rule.conditionId,
                         mContext.getString(R.string.zen_mode_implicit_activated),
-                        Condition.STATE_TRUE);
+                        STATE_TRUE);
 
                 setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP,
                         "applyGlobalZenModeAsImplicitZenRule", callingUid);
@@ -687,7 +692,7 @@
                     // would take effect if changing the global policy.
                     // Note that NotificationManager.Policy cannot have any unset priority
                     // categories, but *can* have unset visual effects, which is why we do this.
-                    newZenPolicy = mConfig.toZenPolicy().overwrittenWith(newZenPolicy);
+                    newZenPolicy = mConfig.getZenPolicy().overwrittenWith(newZenPolicy);
                 }
                 updatePolicy(
                         rule,
@@ -878,7 +883,7 @@
             if (rule == null || !canManageAutomaticZenRule(rule)) {
                 return Condition.STATE_UNKNOWN;
             }
-            return rule.condition != null ? rule.condition.state : Condition.STATE_FALSE;
+            return rule.condition != null ? rule.condition.state : STATE_FALSE;
         }
     }
 
@@ -929,7 +934,7 @@
             Condition condition, @ConfigChangeOrigin int origin, int callingUid) {
         if (rules == null || rules.isEmpty()) return;
 
-        if (Flags.modesApi() && condition.source == Condition.SOURCE_USER_ACTION) {
+        if (Flags.modesApi() && condition.source == SOURCE_USER_ACTION) {
             origin = UPDATE_ORIGIN_USER; // Although coming from app, it's actually a user action.
         }
 
@@ -1285,7 +1290,7 @@
             if (isNew) {
                 // Newly created rule with no provided policy; fill in with the default.
                 zenRule.zenPolicy =
-                        Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy();
+                        Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy();
                 return true;
             }
             // Otherwise, a null policy means no policy changes, so we can stop here.
@@ -1296,7 +1301,7 @@
         // fields in the bitmask should be marked as updated.
         ZenPolicy oldPolicy = zenRule.zenPolicy != null
                 ? zenRule.zenPolicy
-                : (Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy());
+                : (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy());
 
         // If this is updating a rule rather than creating a new one, keep any fields from the
         // old policy if they are unspecified in the new policy. For newly created rules, oldPolicy
@@ -1524,9 +1529,15 @@
                     + " conditionId=" + conditionId + " reason=" + reason
                     + " setRingerMode=" + setRingerMode);
             newConfig = mConfig.copy();
-            if (zenMode == Global.ZEN_MODE_OFF) {
-                newConfig.manualRule = null;
-                if (!Flags.modesUi() || origin != UPDATE_ORIGIN_USER) {
+            if (Flags.modesUi()) {
+                newConfig.manualRule.enabler = caller;
+                newConfig.manualRule.conditionId = conditionId != null ? conditionId : Uri.EMPTY;
+                newConfig.manualRule.pkg = PACKAGE_ANDROID;
+                newConfig.manualRule.zenMode = zenMode;
+                newConfig.manualRule.condition = new Condition(newConfig.manualRule.conditionId, "",
+                        zenMode == Global.ZEN_MODE_OFF ? STATE_FALSE : STATE_TRUE,
+                        origin == UPDATE_ORIGIN_USER ? SOURCE_USER_ACTION : SOURCE_UNKNOWN);
+                if (zenMode == Global.ZEN_MODE_OFF && origin != UPDATE_ORIGIN_USER) {
                     // User deactivation of DND means just turning off the manual DND rule.
                     // For API calls (different origin) keep old behavior of snoozing all rules.
                     for (ZenRule automaticRule : newConfig.automaticRules.values()) {
@@ -1536,20 +1547,51 @@
                     }
                 }
             } else {
-                final ZenRule newRule = new ZenRule();
-                newRule.enabled = true;
-                newRule.zenMode = zenMode;
-                newRule.conditionId = conditionId;
-                newRule.enabler = caller;
-                if (Flags.modesApi()) {
-                    newRule.allowManualInvocation = true;
+                if (zenMode == Global.ZEN_MODE_OFF) {
+                    newConfig.manualRule = null;
+                    // User deactivation of DND means just turning off the manual DND rule.
+                    // For API calls (different origin) keep old behavior of snoozing all rules.
+                    for (ZenRule automaticRule : newConfig.automaticRules.values()) {
+                        if (automaticRule.isAutomaticActive()) {
+                            automaticRule.snoozing = true;
+                        }
+                    }
+
+                } else {
+                    final ZenRule newRule = new ZenRule();
+                    newRule.enabled = true;
+                    newRule.zenMode = zenMode;
+                    newRule.conditionId = conditionId;
+                    newRule.enabler = caller;
+                    if (Flags.modesApi()) {
+                        newRule.allowManualInvocation = true;
+                    }
+                    newConfig.manualRule = newRule;
                 }
-                newConfig.manualRule = newRule;
             }
             setConfigLocked(newConfig, origin, reason, null, setRingerMode, callingUid);
         }
     }
 
+    public void setManualZenRuleDeviceEffects(ZenDeviceEffects deviceEffects,
+            @ConfigChangeOrigin int origin, String reason, int callingUid) {
+        if (!Flags.modesUi()) {
+            return;
+        }
+        ZenModeConfig newConfig;
+        synchronized (mConfigLock) {
+            if (mConfig == null) return;
+            if (DEBUG) Log.d(TAG, "updateManualRule " + deviceEffects
+                    + " reason=" + reason
+                    + " callingUid=" + callingUid);
+            newConfig = mConfig.copy();
+
+            newConfig.manualRule.pkg = PACKAGE_ANDROID;
+            newConfig.manualRule.zenDeviceEffects = deviceEffects;
+            setConfigLocked(newConfig, origin, reason, null, true, callingUid);
+        }
+    }
+
     void dump(ProtoOutputStream proto) {
         proto.write(ZenModeProto.ZEN_MODE, mZenMode);
         synchronized (mConfigLock) {
@@ -1558,7 +1600,7 @@
             }
             for (ZenRule rule : mConfig.automaticRules.values()) {
                 if (rule.enabled && rule.condition != null
-                        && rule.condition.state == Condition.STATE_TRUE
+                        && rule.condition.state == STATE_TRUE
                         && !rule.snoozing) {
                     rule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
                 }
@@ -1592,33 +1634,7 @@
 
     private static void dump(PrintWriter pw, String prefix, String var, ZenModeConfig config) {
         pw.print(prefix); pw.print(var); pw.print('=');
-        if (config == null) {
-            pw.println(config);
-            return;
-        }
-        pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b,"
-                + "messages=%b,messagesFrom=%s,conversations=%b,conversationsFrom=%s,"
-                        + "events=%b,reminders=%b",
-                config.allowAlarms, config.allowMedia, config.allowSystem,
-                config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom),
-                config.allowRepeatCallers, config.allowMessages,
-                ZenModeConfig.sourceToString(config.allowMessagesFrom),
-                config.allowConversations,
-                ZenPolicy.conversationTypeToString(config.allowConversationsFrom),
-                config.allowEvents, config.allowReminders);
-        if (Flags.modesApi()) {
-            pw.printf(",priorityChannels=%b", config.allowPriorityChannels);
-        }
-        pw.printf(")\n");
-        pw.print(prefix);
-        pw.printf("  disallow(visualEffects=%s)\n", config.suppressedVisualEffects);
-        pw.print(prefix); pw.print("  manualRule="); pw.println(config.manualRule);
-        if (config.automaticRules.isEmpty()) return;
-        final int N = config.automaticRules.size();
-        for (int i = 0; i < N; i++) {
-            pw.print(prefix); pw.print(i == 0 ? "  automaticRules=" : "                 ");
-            pw.println(config.automaticRules.valueAt(i));
-        }
+        pw.println(config);
     }
 
     public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
@@ -1629,7 +1645,9 @@
         if (config != null) {
             if (forRestore) {
                 config.user = userId;
-                config.manualRule = null;  // don't restore the manual rule
+                if (!Flags.modesUi()) {
+                    config.manualRule = null;  // don't restore the manual rule
+                }
             }
 
             // booleans to determine whether to reset the rules to the default rules
@@ -1653,7 +1671,7 @@
                     // rules with null ZenPolicies explicitly as a copy of the global policy.
                     if (Flags.modesApi() && config.version < ZenModeConfig.XML_VERSION_MODES_API) {
                         // Keep the manual ("global") policy that from config.
-                        ZenPolicy manualRulePolicy = config.toZenPolicy();
+                        ZenPolicy manualRulePolicy = config.getZenPolicy();
                         if (automaticRule.zenPolicy == null) {
                             automaticRule.zenPolicy = manualRulePolicy;
                         } else {
@@ -1842,7 +1860,7 @@
      */
     @VisibleForTesting
     protected ZenPolicy getDefaultZenPolicy() {
-        return mDefaultConfig.toZenPolicy();
+        return mDefaultConfig.getZenPolicy();
     }
 
     @GuardedBy("mConfigLock")
@@ -2008,7 +2026,7 @@
     private int computeZenMode() {
         synchronized (mConfigLock) {
             if (mConfig == null) return Global.ZEN_MODE_OFF;
-            if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
+            if (mConfig.isManualActive()) return mConfig.manualRule.zenMode;
             int zen = Global.ZEN_MODE_OFF;
             for (ZenRule automaticRule : mConfig.automaticRules.values()) {
                 if (automaticRule.isAutomaticActive()) {
@@ -2047,19 +2065,19 @@
             if (Flags.modesApi()) {
                 if (useManualConfig) {
                     // manual rule is configured using the settings stored directly in mConfig
-                    policy.apply(mConfig.toZenPolicy());
+                    policy.apply(mConfig.getZenPolicy());
                 } else {
                     // under modes_api flag, an active automatic rule with no specified policy
                     // inherits the device default settings as stored in mDefaultConfig. While the
                     // rule's policy fields should be set upon creation, this is a fallback to
                     // catch any that may have fallen through the cracks.
                     Log.wtf(TAG, "active automatic rule found with no specified policy: " + rule);
-                    policy.apply(
-                            Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy());
+                    policy.apply(Flags.modesUi()
+                            ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy());
                 }
             } else {
-                // active rule with no specified policy inherits the global config settings
-                policy.apply(mConfig.toZenPolicy());
+                // active rule with no specified policy inherits the manual rule config settings
+                policy.apply(mConfig.getZenPolicy());
             }
         }
     }
@@ -2071,7 +2089,7 @@
             if (mConfig == null) return;
             ZenPolicy policy = new ZenPolicy();
             ZenDeviceEffects.Builder deviceEffectsBuilder = new ZenDeviceEffects.Builder();
-            if (mConfig.manualRule != null) {
+            if (mConfig.isManualActive()) {
                 applyCustomPolicy(policy, mConfig.manualRule, true);
                 if (Flags.modesApi()) {
                     deviceEffectsBuilder.add(mConfig.manualRule.zenDeviceEffects);
@@ -2154,7 +2172,7 @@
             // Should be checked before calling, but just in case.
             return;
         }
-        ZenPolicy defaultPolicy = mDefaultConfig.toZenPolicy();
+        ZenPolicy defaultPolicy = mDefaultConfig.getZenPolicy();
         for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
             if (ZenModeConfig.DEFAULT_RULE_IDS.contains(rule.id) && rule.zenPolicy == null) {
                 rule.zenPolicy = defaultPolicy.copy();
@@ -2335,17 +2353,17 @@
                 final ZenModeConfig config = mConfigs.valueAt(i);
                 events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE,
                         /* optional int32 user = 1 */ user,
-                        /* optional bool enabled = 2 */ config.manualRule != null,
+                        /* optional bool enabled = 2 */ config.isManualActive(),
                         /* optional bool channels_bypassing = 3 */ config.areChannelsBypassingDnd,
                         /* optional LoggedZenMode zen_mode = 4 */ ROOT_CONFIG,
                         /* optional string id = 5 */ "", // empty for root config
                         /* optional int32 uid = 6 */ Process.SYSTEM_UID, // system owns root config
-                        /* optional DNDPolicyProto policy = 7 */ config.toZenPolicy().toProto(),
+                        /* optional DNDPolicyProto policy = 7 */ config.getZenPolicy().toProto(),
                         /* optional int32 rule_modified_fields = 8 */ 0,
                         /* optional int32 policy_modified_fields = 9 */ 0,
                         /* optional int32 device_effects_modified_fields = 10 */ 0,
                         /* optional ActiveRuleType rule_type = 11 */ TYPE_UNKNOWN));
-                if (config.manualRule != null) {
+                if (config.isManualActive()) {
                     ruleToProtoLocked(user, config.manualRule, true, events);
                 }
                 for (ZenRule rule : config.automaticRules.values()) {
diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
index 96ab2cc..7dd8f2f 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -188,7 +188,8 @@
     public static IStreamingResponseCallback wrapWithValidation(
             IStreamingResponseCallback streamingResponseCallback,
             Executor resourceClosingExecutor,
-            AndroidFuture future) {
+            AndroidFuture future,
+            InferenceInfoStore inferenceInfoStore) {
         return new IStreamingResponseCallback.Stub() {
             @Override
             public void onNewContent(Bundle processedResult) throws RemoteException {
@@ -207,6 +208,7 @@
                     sanitizeResponseParams(resultBundle);
                     streamingResponseCallback.onSuccess(resultBundle);
                 } finally {
+                    inferenceInfoStore.addInferenceInfoFromBundle(resultBundle);
                     resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
                     future.complete(null);
                 }
@@ -216,6 +218,7 @@
             public void onFailure(int errorCode, String errorMessage,
                     PersistableBundle errorParams) throws RemoteException {
                 streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams);
+                inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
                 future.completeExceptionally(new TimeoutException());
             }
 
@@ -245,7 +248,8 @@
 
     public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback,
             Executor resourceClosingExecutor,
-            AndroidFuture future) {
+            AndroidFuture future,
+            InferenceInfoStore inferenceInfoStore) {
         return new IResponseCallback.Stub() {
             @Override
             public void onSuccess(Bundle resultBundle)
@@ -254,6 +258,7 @@
                     sanitizeResponseParams(resultBundle);
                     responseCallback.onSuccess(resultBundle);
                 } finally {
+                    inferenceInfoStore.addInferenceInfoFromBundle(resultBundle);
                     resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
                     future.complete(null);
                 }
@@ -263,6 +268,7 @@
             public void onFailure(int errorCode, String errorMessage,
                     PersistableBundle errorParams) throws RemoteException {
                 responseCallback.onFailure(errorCode, errorMessage, errorParams);
+                inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
                 future.completeExceptionally(new TimeoutException());
             }
 
@@ -291,11 +297,13 @@
 
 
     public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback,
-            AndroidFuture future) {
+            AndroidFuture future,
+            InferenceInfoStore inferenceInfoStore) {
         return new ITokenInfoCallback.Stub() {
             @Override
             public void onSuccess(TokenInfo tokenInfo) throws RemoteException {
                 responseCallback.onSuccess(tokenInfo);
+                inferenceInfoStore.addInferenceInfoFromBundle(tokenInfo.getInfoParams());
                 future.complete(null);
             }
 
@@ -303,6 +311,7 @@
             public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams)
                     throws RemoteException {
                 responseCallback.onFailure(errorCode, errorMessage, errorParams);
+                inferenceInfoStore.addInferenceInfoFromBundle(errorParams);
                 future.completeExceptionally(new TimeoutException());
             }
         };
diff --git a/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java b/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
new file mode 100644
index 0000000..b532d5a
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/InferenceInfoStore.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.InferenceInfo;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+import android.util.Slog;
+import android.util.Base64;
+
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.List;
+import java.util.TreeSet;
+
+public class InferenceInfoStore {
+    private static final String TAG = "InferenceInfoStore";
+    private final TreeSet<InferenceInfo> inferenceInfos;
+    private final long maxAgeMs;
+
+    public InferenceInfoStore(long maxAgeMs) {
+        this.maxAgeMs = maxAgeMs;
+        this.inferenceInfos = new TreeSet<>(
+                Comparator.comparingLong(InferenceInfo::getStartTimeMs));
+    }
+
+    public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+        return inferenceInfos.stream().filter(
+                info -> info.getStartTimeMs() > startTimeEpochMillis).toList();
+    }
+
+    public void addInferenceInfoFromBundle(PersistableBundle pb) {
+        if (!pb.containsKey(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY)) {
+            return;
+        }
+
+        try {
+            String infoBytesBase64String = pb.getString(
+                    OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
+            if (infoBytesBase64String != null) {
+                byte[] infoBytes = Base64.decode(infoBytesBase64String, Base64.DEFAULT);
+                com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+                        com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
+                                infoBytes);
+                add(inferenceInfo);
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to parse InferenceInfo from the received bytes.");
+        }
+    }
+
+    public void addInferenceInfoFromBundle(Bundle b) {
+        if (!b.containsKey(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY)) {
+            return;
+        }
+
+        try {
+            byte[] infoBytes = b.getByteArray(
+                    OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY);
+            if (infoBytes != null) {
+                com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+                        com.android.server.ondeviceintelligence.nano.InferenceInfo.parseFrom(
+                                infoBytes);
+                add(inferenceInfo);
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to parse InferenceInfo from the received bytes.");
+        }
+    }
+
+    private synchronized void add(com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
+        while (!inferenceInfos.isEmpty()
+                && System.currentTimeMillis() - inferenceInfos.first().getStartTimeMs()
+                > maxAgeMs) {
+            inferenceInfos.pollFirst();
+        }
+        inferenceInfos.add(toInferenceInfo(info));
+    }
+
+    private static InferenceInfo toInferenceInfo(
+            com.android.server.ondeviceintelligence.nano.InferenceInfo info) {
+        return new InferenceInfo.Builder().setUid(info.uid).setStartTimeMs(
+                info.startTimeMs).setEndTimeMs(info.endTimeMs).setSuspendedTimeMs(
+                info.suspendedTimeMs).build();
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
index 81f11b5..1450dc0 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerInternal.java
@@ -17,5 +17,10 @@
 package com.android.server.ondeviceintelligence;
 
 public interface OnDeviceIntelligenceManagerInternal {
-    String getRemoteServicePackageName();
+    /**
+     * Gets the uid for the process that is currently hosting the
+     * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} registered on
+     * the device.
+     */
+    int getInferenceServiceUid();
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index f540f1d..9ef2e12 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -44,6 +44,7 @@
 import android.app.ondeviceintelligence.IResponseCallback;
 import android.app.ondeviceintelligence.IStreamingResponseCallback;
 import android.app.ondeviceintelligence.ITokenInfoCallback;
+import android.app.ondeviceintelligence.InferenceInfo;
 import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -56,6 +57,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -126,6 +128,7 @@
     private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
 
     private static final String SYSTEM_PACKAGE = "android";
+    private static final long MAX_AGE_MS = TimeUnit.HOURS.toMillis(3);
 
 
     private final Executor resourceClosingExecutor = Executors.newCachedThreadPool();
@@ -137,12 +140,15 @@
     private final Context mContext;
     protected final Object mLock = new Object();
 
-
+    private final InferenceInfoStore mInferenceInfoStore;
     private RemoteOnDeviceSandboxedInferenceService mRemoteInferenceService;
     private RemoteOnDeviceIntelligenceService mRemoteOnDeviceIntelligenceService;
     volatile boolean mIsServiceEnabled;
 
     @GuardedBy("mLock")
+    private int remoteInferenceServiceUid = -1;
+
+    @GuardedBy("mLock")
     private String[] mTemporaryServiceNames;
     @GuardedBy("mLock")
     private String[] mTemporaryBroadcastKeys;
@@ -166,6 +172,7 @@
         super(context);
         mContext = context;
         mTemporaryServiceNames = new String[0];
+        mInferenceInfoStore = new InferenceInfoStore(MAX_AGE_MS);
     }
 
     @Override
@@ -174,7 +181,7 @@
                 Context.ON_DEVICE_INTELLIGENCE_SERVICE, getOnDeviceIntelligenceManagerService(),
                 /* allowIsolated = */true);
         LocalServices.addService(OnDeviceIntelligenceManagerInternal.class,
-                OnDeviceIntelligenceManagerService.this::getRemoteConfiguredPackageName);
+                this::getRemoteInferenceServiceUid);
     }
 
     @Override
@@ -187,6 +194,16 @@
 
             mIsServiceEnabled = isServiceEnabled();
         }
+
+        //connect to remote services(if available) during boot phase.
+        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            try {
+                ensureRemoteInferenceServiceInitialized();
+                ensureRemoteIntelligenceServiceInitialized();
+            } catch (Exception e) {
+                Slog.w(TAG, "Couldn't pre-start remote ondeviceintelligence services.", e);
+            }
+        }
     }
 
     private void onDeviceConfigChange(@NonNull Set<String> keys) {
@@ -209,10 +226,18 @@
             }
 
             @Override
+            public List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+                mContext.enforceCallingPermission(
+                        Manifest.permission.DUMP, TAG);
+                return OnDeviceIntelligenceManagerService.this.getLatestInferenceInfo(
+                        startTimeEpochMillis);
+            }
+
+            @Override
             public void getVersion(RemoteCallback remoteCallback) {
                 Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion");
                 Objects.requireNonNull(remoteCallback);
-                mContext.enforceCallingOrSelfPermission(
+                mContext.enforceCallingPermission(
                         Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
                 if (!mIsServiceEnabled) {
                     Slog.w(TAG, "Service not available");
@@ -237,7 +262,7 @@
                     throws RemoteException {
                 Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
                 Objects.requireNonNull(featureCallback);
-                mContext.enforceCallingOrSelfPermission(
+                mContext.enforceCallingPermission(
                         Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
                 if (!mIsServiceEnabled) {
                     Slog.w(TAG, "Service not available");
@@ -275,7 +300,7 @@
                     throws RemoteException {
                 Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
                 Objects.requireNonNull(listFeaturesCallback);
-                mContext.enforceCallingOrSelfPermission(
+                mContext.enforceCallingPermission(
                         Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
                 if (!mIsServiceEnabled) {
                     Slog.w(TAG, "Service not available");
@@ -319,7 +344,7 @@
                 Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatureStatus");
                 Objects.requireNonNull(feature);
                 Objects.requireNonNull(featureDetailsCallback);
-                mContext.enforceCallingOrSelfPermission(
+                mContext.enforceCallingPermission(
                         Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
                 if (!mIsServiceEnabled) {
                     Slog.w(TAG, "Service not available");
@@ -363,7 +388,7 @@
                 Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload");
                 Objects.requireNonNull(feature);
                 Objects.requireNonNull(downloadCallback);
-                mContext.enforceCallingOrSelfPermission(
+                mContext.enforceCallingPermission(
                         Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
                 if (!mIsServiceEnabled) {
                     Slog.w(TAG, "Service not available");
@@ -403,7 +428,7 @@
                     sanitizeInferenceParams(request);
                     Objects.requireNonNull(tokenInfoCallback);
 
-                    mContext.enforceCallingOrSelfPermission(
+                    mContext.enforceCallingPermission(
                             Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
                     if (!mIsServiceEnabled) {
                         Slog.w(TAG, "Service not available");
@@ -420,7 +445,8 @@
                                 service.requestTokenInfo(callerUid, feature,
                                         request,
                                         wrapCancellationFuture(cancellationSignalFuture),
-                                        wrapWithValidation(tokenInfoCallback, future));
+                                        wrapWithValidation(tokenInfoCallback, future,
+                                                mInferenceInfoStore));
                                 return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
                             });
                     result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
@@ -446,7 +472,7 @@
                     Objects.requireNonNull(feature);
                     sanitizeInferenceParams(request);
                     Objects.requireNonNull(responseCallback);
-                    mContext.enforceCallingOrSelfPermission(
+                    mContext.enforceCallingPermission(
                             Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
                     if (!mIsServiceEnabled) {
                         Slog.w(TAG, "Service not available");
@@ -466,7 +492,8 @@
                                         wrapCancellationFuture(cancellationSignalFuture),
                                         wrapProcessingFuture(processingSignalFuture),
                                         wrapWithValidation(responseCallback,
-                                                resourceClosingExecutor, future));
+                                                resourceClosingExecutor, future,
+                                                mInferenceInfoStore));
                                 return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
                             });
                     result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
@@ -491,7 +518,7 @@
                     Objects.requireNonNull(feature);
                     sanitizeInferenceParams(request);
                     Objects.requireNonNull(streamingCallback);
-                    mContext.enforceCallingOrSelfPermission(
+                    mContext.enforceCallingPermission(
                             Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
                     if (!mIsServiceEnabled) {
                         Slog.w(TAG, "Service not available");
@@ -511,7 +538,8 @@
                                         wrapCancellationFuture(cancellationSignalFuture),
                                         wrapProcessingFuture(processingSignalFuture),
                                         wrapWithValidation(streamingCallback,
-                                                resourceClosingExecutor, future));
+                                                resourceClosingExecutor, future,
+                                                mInferenceInfoStore));
                                 return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
                             });
                     result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
@@ -603,7 +631,13 @@
                                 try {
                                     ensureRemoteIntelligenceServiceInitialized();
                                     service.registerRemoteStorageService(
-                                            getIRemoteStorageService());
+                                            getIRemoteStorageService(), new IRemoteCallback.Stub() {
+                                                @Override
+                                                public void sendResult(Bundle bundle) {
+                                                    final int uid = Binder.getCallingUid();
+                                                    setRemoteInferenceServiceUid(uid);
+                                                }
+                                            });
                                     mRemoteOnDeviceIntelligenceService.run(
                                             IOnDeviceIntelligenceService::notifyInferenceServiceConnected);
                                     broadcastExecutor.execute(
@@ -826,6 +860,10 @@
                 && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
     }
 
+    private List<InferenceInfo> getLatestInferenceInfo(long startTimeEpochMillis) {
+        return mInferenceInfoStore.getLatestInferenceInfo(startTimeEpochMillis);
+    }
+
     @Nullable
     public String getRemoteConfiguredPackageName() {
         try {
@@ -1038,4 +1076,16 @@
                 Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1),
                 mContext.getUserId());
     }
+
+    private int getRemoteInferenceServiceUid() {
+        synchronized (mLock) {
+            return remoteInferenceServiceUid;
+        }
+    }
+
+    private void setRemoteInferenceServiceUid(int remoteInferenceServiceUid) {
+        synchronized (mLock) {
+            this.remoteInferenceServiceUid = remoteInferenceServiceUid;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/os/OWNERS b/services/core/java/com/android/server/os/OWNERS
index 70be161..e130d9c0 100644
--- a/services/core/java/com/android/server/os/OWNERS
+++ b/services/core/java/com/android/server/os/OWNERS
@@ -2,4 +2,4 @@
 per-file Bugreport* = file:/platform/frameworks/native:/cmds/dumpstate/OWNERS
 
 # NativeTombstone
-per-file NativeTombstone* = gaillard@google.com
+per-file NativeTombstone* = benmiles@google.com
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index e2a6b81..f59ae16 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -4364,7 +4364,7 @@
             uid = getBaseSdkSandboxUid();
         }
         final int callingUserId = UserHandle.getUserId(callingUid);
-        if (isKnownIsolatedComputeApp(uid, callingUserId)) {
+        if (isKnownIsolatedComputeApp(uid)) {
             try {
                 uid = getIsolatedOwner(uid);
             } catch (IllegalStateException e) {
@@ -4407,7 +4407,7 @@
             if (Process.isSdkSandboxUid(uid)) {
                 uid = getBaseSdkSandboxUid();
             }
-            if (isKnownIsolatedComputeApp(uid, callingUserId)) {
+            if (isKnownIsolatedComputeApp(uid)) {
                 try {
                     uid = getIsolatedOwner(uid);
                 } catch (IllegalStateException e) {
@@ -5809,7 +5809,7 @@
     }
 
 
-    private boolean isKnownIsolatedComputeApp(int uid, int callingUserId) {
+    private boolean isKnownIsolatedComputeApp(int uid) {
         if (!Process.isIsolatedUid(uid)) {
             return false;
         }
@@ -5822,27 +5822,8 @@
         }
         OnDeviceIntelligenceManagerInternal onDeviceIntelligenceManagerInternal =
                 mInjector.getLocalService(OnDeviceIntelligenceManagerInternal.class);
-        if (onDeviceIntelligenceManagerInternal == null) {
-            return false;
-        }
-
-        String onDeviceIntelligencePackage =
-                onDeviceIntelligenceManagerInternal.getRemoteServicePackageName();
-        if (onDeviceIntelligencePackage == null) {
-            return false;
-        }
-
-        try {
-            if (getIsolatedOwner(uid) == getPackageUid(onDeviceIntelligencePackage, 0,
-                    callingUserId)) {
-                return true;
-            }
-        } catch (IllegalStateException e) {
-            // If the owner uid doesn't exist, just use the current uid
-            Slog.wtf(TAG, "Expected isolated uid " + uid + " to have an owner", e);
-        }
-
-        return false;
+        return onDeviceIntelligenceManagerInternal != null
+                && uid == onDeviceIntelligenceManagerInternal.getInferenceServiceUid();
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 209cbb7..e34bdc6 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -728,7 +728,14 @@
         final int compilationReason =
                 dexManager.getCompilationReasonForInstallScenario(
                         installRequest.getInstallScenario());
-        return new DexoptOptions(packageName, compilationReason, dexoptFlags);
+        final AndroidPackage pkg = ps.getPkg();
+        var options = new DexoptOptions(packageName, compilationReason, dexoptFlags);
+        if (installRequest.getDexoptCompilerFilter() != null) {
+            options = options.overrideCompilerFilter(installRequest.getDexoptCompilerFilter());
+        } else if (pkg != null && pkg.isDebuggable()) {
+            options = options.overrideCompilerFilter(DexoptParams.COMPILER_FILTER_NOOP);
+        }
+        return options;
     }
 
     /**
@@ -772,12 +779,12 @@
                 && installRequest.getInstallSource().mInitiatingPackageName.equals("android"))
                 : true;
 
+        // Don't skip the dexopt call if the compiler filter is "skip". Instead, call dexopt with
+        // the "skip" filter so that ART Service gets notified and skips dexopt itself.
         return (!instantApp || Global.getInt(context.getContentResolver(),
                 Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                 && pkg != null
-                && !pkg.isDebuggable()
                 && (!onIncremental)
-                && dexoptOptions.isCompilationEnabled()
                 && !isApex
                 && performDexOptForRollback;
     }
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index 46f9732..8001615 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -58,6 +58,8 @@
     final int mDataLoaderType;
     final int mPackageSource;
     final boolean mApplicationEnabledSettingPersistent;
+    @Nullable
+    final String mDexoptCompilerFilter;
 
     // The list of instruction sets supported by this app. This is currently
     // only used during the rmdex() phase to clean up resources. We can get rid of this
@@ -73,7 +75,7 @@
             int autoRevokePermissionsMode, String traceMethod, int traceCookie,
             SigningDetails signingDetails, int installReason, int installScenario,
             boolean forceQueryableOverride, int dataLoaderType, int packageSource,
-            boolean applicationEnabledSettingPersistent) {
+            boolean applicationEnabledSettingPersistent, String dexoptCompilerFilter) {
         mOriginInfo = originInfo;
         mMoveInfo = moveInfo;
         mInstallFlags = installFlags;
@@ -96,5 +98,6 @@
         mDataLoaderType = dataLoaderType;
         mPackageSource = packageSource;
         mApplicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
+        mDexoptCompilerFilter = dexoptCompilerFilter;
     }
 }
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 8f51e36..dd2583a0d 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -174,13 +174,13 @@
         mUserId = params.getUser().getIdentifier();
         mInstallArgs = new InstallArgs(params.mOriginInfo, params.mMoveInfo, params.mObserver,
                 params.mInstallFlags, params.mDevelopmentInstallFlags, params.mInstallSource,
-                params.mVolumeUuid,  params.getUser(), null /*instructionSets*/,
+                params.mVolumeUuid, params.getUser(), null /*instructionSets*/,
                 params.mPackageAbiOverride, params.mPermissionStates,
                 params.mAllowlistedRestrictedPermissions, params.mAutoRevokePermissionsMode,
                 params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
                 params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
                 params.mDataLoaderType, params.mPackageSource,
-                params.mApplicationEnabledSettingPersistent);
+                params.mApplicationEnabledSettingPersistent, params.mDexoptCompilerFilter);
         mPackageLite = params.mPackageLite;
         mPackageMetrics = new PackageMetrics(this);
         mIsInstallInherit = params.mIsInherit;
@@ -709,6 +709,11 @@
         return mWarnings;
     }
 
+    @Nullable
+    public String getDexoptCompilerFilter() {
+        return mInstallArgs != null ? mInstallArgs.mDexoptCompilerFilter : null;
+    }
+
     public void setScanFlags(int scanFlags) {
         mScanFlags = scanFlags;
     }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 8038c9a..6d79139 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -408,16 +408,6 @@
         }
     }
 
-    public void restoreconAppData(String uuid, String packageName, int userId, int flags, int appId,
-            String seInfo) throws InstallerException {
-        if (!checkBeforeRemote()) return;
-        try {
-            mInstalld.restoreconAppData(uuid, packageName, userId, flags, appId, seInfo);
-        } catch (Exception e) {
-            throw InstallerException.from(e);
-        }
-    }
-
     public void migrateAppData(String uuid, String packageName, int userId, int flags)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index d3a18f9..ccc1175 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -102,6 +102,7 @@
     @Nullable
     final DomainSet mPreVerifiedDomains;
     final boolean mHasAppMetadataFile;
+    @Nullable final String mDexoptCompilerFilter;
 
     // For move install
     InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
@@ -136,6 +137,7 @@
         mApplicationEnabledSettingPersistent = false;
         mPreVerifiedDomains = null;
         mHasAppMetadataFile = false;
+        mDexoptCompilerFilter = null;
     }
 
     InstallingSession(int sessionId, File stagedDir, IPackageInstallObserver2 observer,
@@ -172,6 +174,7 @@
         mApplicationEnabledSettingPersistent = sessionParams.applicationEnabledSettingPersistent;
         mPreVerifiedDomains = preVerifiedDomains;
         mHasAppMetadataFile = hasAppMetadatafile;
+        mDexoptCompilerFilter = sessionParams.dexoptCompilerFilter;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/KillAppBlocker.java b/services/core/java/com/android/server/pm/KillAppBlocker.java
index e2901c3..7f5ad9d 100644
--- a/services/core/java/com/android/server/pm/KillAppBlocker.java
+++ b/services/core/java/com/android/server/pm/KillAppBlocker.java
@@ -83,13 +83,13 @@
         }
     }
 
-    void waitAppProcessGone(ActivityManagerInternal mAmi, Computer snapshot,
+    void waitAppProcessGone(ActivityManagerInternal ami, Computer snapshot,
             UserManagerService userManager, String packageName) {
         if (!mRegistered) {
             return;
         }
         synchronized (this) {
-            if (mAmi != null) {
+            if (ami != null) {
                 int[] users = userManager.getUserIds();
 
                 for (int i = 0; i < users.length; i++) {
@@ -97,12 +97,16 @@
                     final int uid = snapshot.getPackageUidInternal(
                             packageName, MATCH_ALL, userId, Process.SYSTEM_UID);
                     if (uid != INVALID_UID) {
-                        if (mAmi.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) {
+                        if (ami.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) {
                             mActiveUids.add(uid);
                         }
                     }
                 }
             }
+            if (mActiveUids.size() == 0) {
+                // no active uid
+                return;
+            }
         }
 
         try {
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index 0afda45..11f2059 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -62,6 +62,12 @@
 
     PackageFreezer(String packageName, int userId, String killReason,
             PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request) {
+        this(packageName, userId, killReason, pm, exitInfoReason, request, false);
+    }
+
+    PackageFreezer(String packageName, int userId, String killReason,
+            PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request,
+            boolean waitAppKilled) {
         mPm = pm;
         mPackageName = packageName;
         mInstallRequest = request;
@@ -77,7 +83,7 @@
             ps = mPm.mSettings.getPackageLPr(mPackageName);
         }
         if (ps != null) {
-            if (Flags.waitApplicationKilled()) {
+            if (waitAppKilled && Flags.waitApplicationKilled()) {
                 mPm.killApplicationSync(ps.getPackageName(), ps.getAppId(), userId, killReason,
                         exitInfoReason);
             } else {
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 68f6ca1..0a0882d 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -123,19 +123,10 @@
                 }
             } break;
             case WRITE_SETTINGS: {
-                if (!mPm.tryWriteSettings(/*sync=*/false)) {
-                    // Failed to write.
-                    this.removeMessages(WRITE_SETTINGS);
-                    mPm.scheduleWriteSettings();
-                }
+                mPm.writeSettings(/*sync=*/false);
             } break;
             case WRITE_PACKAGE_LIST: {
-                int userId = msg.arg1;
-                if (!mPm.tryWritePackageList(userId)) {
-                    // Failed to write.
-                    this.removeMessages(WRITE_PACKAGE_LIST);
-                    mPm.scheduleWritePackageList(userId);
-                }
+                mPm.writePackageList(msg.arg1);
             } break;
             case CHECK_PENDING_VERIFICATION: {
                 final int verificationId = msg.arg1;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 050d44e..b93dcdc 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1423,7 +1423,9 @@
         DevicePolicyManagerInternal dpmi =
                 LocalServices.getService(DevicePolicyManagerInternal.class);
         final boolean canSilentlyInstallPackage =
-                dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid);
+                (dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid))
+                        || PackageInstallerSession.isEmergencyInstallerEnabled(
+                        versionedPackage.getPackageName(), snapshot, userId, callingUid);
 
         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
                 statusReceiver, versionedPackage.getPackageName(),
@@ -1445,15 +1447,6 @@
                     .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE)
                     .setAdmin(callerPackageName)
                     .write();
-        } else if (PackageInstallerSession.isEmergencyInstallerEnabled(callerPackageName, snapshot,
-                userId, callingUid)) {
-            // Need to clear the calling identity to get DELETE_PACKAGES permission
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
         } else {
             ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId);
             if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0606563..00e9d8d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -33,6 +33,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PRE_APPROVAL_NOT_AVAILABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_STAGED;
@@ -3459,11 +3460,6 @@
             }
         }
 
-        if (mHasAppMetadataFile && !getStagedAppMetadataFile().exists()) {
-            throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
-                    "App metadata file expected but not found in " + stageDir.getAbsolutePath());
-        }
-
         final List<ApkLite> addedFiles = getAddedApkLitesLocked();
         if (addedFiles.isEmpty()
                 && (removeSplitList.size() == 0 || mHasAppMetadataFile)) {
@@ -3593,6 +3589,13 @@
             }
         }
 
+        File stagedAppMetadataFile = isIncrementalInstallation()
+                ? getTmpAppMetadataFile() : getStagedAppMetadataFile();
+        if (mHasAppMetadataFile && !stagedAppMetadataFile.exists()) {
+            throw new PackageManagerException(INSTALL_FAILED_SESSION_INVALID,
+                    "App metadata file expected but not found in " + stageDir.getAbsolutePath());
+        }
+
         if (isIncrementalInstallation()) {
             if (!isIncrementalInstallationAllowed(existingPkgSetting)) {
                 throw new PackageManagerException(
@@ -3601,8 +3604,8 @@
             }
             // Since we moved the staged app metadata file so that incfs can be initialized, lets
             // now move it back.
-            File appMetadataFile = getTmpAppMetadataFile();
-            if (appMetadataFile.exists()) {
+            if (mHasAppMetadataFile) {
+                File appMetadataFile = getTmpAppMetadataFile();
                 final IncrementalFileStorages incrementalFileStorages =
                         getIncrementalFileStorages();
                 try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2b0e62c..c0b8034 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -485,9 +485,6 @@
      */
     static final long WATCHDOG_TIMEOUT = 1000*60*10;     // ten minutes
 
-    // How long to wait for Lock in async writeSettings and writePackageList.
-    private static final long WRITE_LOCK_TIMEOUT_MS = 1000 * 10;   // 10 seconds
-
     /**
      * Default IncFs timeouts. Maximum values in IncFs is 1hr.
      *
@@ -1576,7 +1573,7 @@
         }
     }
 
-    void scheduleWritePackageList(int userId) {
+    void scheduleWritePackageListLocked(int userId) {
         invalidatePackageInfoCache();
         if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
             Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST);
@@ -1628,42 +1625,22 @@
         mSettings.writePackageRestrictions(dirtyUsers);
     }
 
-    private boolean tryUnderLock(boolean sync, long timeoutMs, Runnable runnable) {
-        try {
-            PackageManagerTracedLock.RawLock lock = mLock.getRawLock();
-            if (sync) {
-                lock.lock();
-            } else if (!lock.tryLock(timeoutMs, TimeUnit.MILLISECONDS)) {
-                return false;
-            }
-            try {
-                runnable.run();
-                return true;
-            } finally {
-                lock.unlock();
-            }
-        } catch (InterruptedException e) {
-            Slog.e(TAG, "Failed to obtain mLock", e);
-        }
-        return false;
-    }
-
-    boolean tryWriteSettings(boolean sync) {
-        return tryUnderLock(sync, WRITE_LOCK_TIMEOUT_MS, () -> {
+    void writeSettings(boolean sync) {
+        synchronized (mLock) {
             mHandler.removeMessages(WRITE_SETTINGS);
             mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS);
             writeSettingsLPrTEMP(sync);
             synchronized (mDirtyUsers) {
                 mDirtyUsers.clear();
             }
-        });
+        }
     }
 
-    boolean tryWritePackageList(int userId) {
-        return tryUnderLock(/*sync=*/false, WRITE_LOCK_TIMEOUT_MS, () -> {
+    void writePackageList(int userId) {
+        synchronized (mLock) {
             mHandler.removeMessages(WRITE_PACKAGE_LIST);
             mSettings.writePackageListLPr(userId);
-        });
+        }
     }
 
     private static final Handler.Callback BACKGROUND_HANDLER_CALLBACK = new Handler.Callback() {
@@ -3068,9 +3045,7 @@
             if (mHandler.hasMessages(WRITE_SETTINGS)
                     || mBackgroundHandler.hasMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS)
                     || mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
-                while (!tryWriteSettings(/*sync=*/true)) {
-                    Slog.wtf(TAG, "Failed to write settings on shutdown");
-                }
+                writeSettings(/*sync=*/true);
             }
         }
     }
@@ -4024,7 +3999,9 @@
                 final PackageMetrics.ComponentStateMetrics componentStateMetrics =
                         new PackageMetrics.ComponentStateMetrics(setting,
                                 UserHandle.getUid(userId, packageSetting.getAppId()),
-                                packageSetting.getEnabled(userId), callingUid);
+                                setting.isComponent() ? computer.getComponentEnabledSettingInternal(
+                                        setting.getComponentName(), callingUid, userId)
+                                        : packageSetting.getEnabled(userId), callingUid);
                 if (!setEnabledSettingInternalLocked(computer, packageSetting, setting, userId,
                         callingPackage)) {
                     continue;
@@ -4372,7 +4349,14 @@
 
     public PackageFreezer freezePackage(String packageName, int userId, String killReason,
             int exitInfoReason, InstallRequest request) {
-        return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request);
+        return freezePackage(packageName, userId, killReason, exitInfoReason, request,
+                /* waitAppKilled= */ false);
+    }
+
+    private PackageFreezer freezePackage(String packageName, int userId, String killReason,
+            int exitInfoReason, InstallRequest request, boolean waitAppKilled) {
+        return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request,
+                waitAppKilled);
     }
 
     public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags,
@@ -4432,7 +4416,7 @@
         }
         synchronized (mLock) {
             scheduleWritePackageRestrictions(userId);
-            scheduleWritePackageList(userId);
+            scheduleWritePackageListLocked(userId);
             mAppsFilter.onUserCreated(snapshotComputer(), userId);
         }
     }
@@ -4797,7 +4781,8 @@
                     final boolean succeeded;
                     try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL,
                             "clearApplicationUserData",
-                            ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */)) {
+                            ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */,
+                            /* waitAppKilled= */ true)) {
                         try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
                             succeeded = clearApplicationUserDataLIF(snapshotComputer(), packageName,
                                     userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index e2ddba5..819a75c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -18,7 +18,7 @@
 
 import android.os.SystemProperties;
 
-import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.art.model.DexoptParams;
 
 import dalvik.system.DexFile;
 
@@ -71,7 +71,7 @@
     private static String getAndCheckValidity(int reason) {
         String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
         if (sysPropValue == null || sysPropValue.isEmpty()
-                || !(sysPropValue.equals(DexoptOptions.COMPILER_FILTER_NOOP)
+                || !(sysPropValue.equals(DexoptParams.COMPILER_FILTER_NOOP)
                         || DexFile.isValidCompilerFilter(sysPropValue))) {
             throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
                     + "(reason " + REASON_STRINGS[reason] + ")");
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index c40563f..7a53fe7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -121,6 +121,8 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.art.ArtManagerLocal;
+import com.android.server.art.ReasonMapping;
+import com.android.server.art.model.DexoptParams;
 import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 import com.android.server.pm.permission.PermissionAllowlist;
@@ -330,6 +332,8 @@
                     return runGetOemPermissions();
                 case "get-signature-permission-allowlist":
                     return runGetSignaturePermissionAllowlist();
+                case "get-shared-uid-allowlist":
+                    return runGetSharedUidAllowlist();
                 case "trim-caches":
                     return runTrimCaches();
                 case "create-user":
@@ -2944,6 +2948,9 @@
             case "system-ext":
                 allowlist = permissionAllowlist.getSystemExtSignatureAppAllowlist();
                 break;
+            case "apex":
+                allowlist = permissionAllowlist.getApexSignatureAppAllowlist();
+                break;
             default:
                 getErrPrintWriter().println("Error: unknown partition: " + partition);
                 return 1;
@@ -2970,6 +2977,20 @@
         return 0;
     }
 
+    private int runGetSharedUidAllowlist() {
+        final var allowlist = SystemConfig.getInstance().getPackageToSharedUidAllowList();
+        final var pw = getOutPrintWriter();
+        final var allowlistSize = allowlist.size();
+        for (var allowlistIndex = 0; allowlistIndex < allowlistSize; allowlistIndex++) {
+            final var packageName = allowlist.keyAt(allowlistIndex);
+            final var sharedUserName = allowlist.valueAt(allowlistIndex);
+            pw.print(packageName);
+            pw.print(" ");
+            pw.println(sharedUserName);
+        }
+        return 0;
+    }
+
     private int runTrimCaches() throws RemoteException {
         String size = getNextArg();
         if (size == null) {
@@ -3570,6 +3591,14 @@
                 case "--package-source":
                     sessionParams.setPackageSource(Integer.parseInt(getNextArg()));
                     break;
+                case "--dexopt-compiler-filter":
+                    sessionParams.dexoptCompilerFilter = getNextArgRequired();
+                    // An early check that throws IllegalArgumentException if the compiler filter is
+                    // invalid.
+                    new DexoptParams.Builder(ReasonMapping.REASON_INSTALL)
+                            .setCompilerFilter(sessionParams.dexoptCompilerFilter)
+                            .build();
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
@@ -4716,6 +4745,7 @@
         pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
         pw.println("       [--apex] [--non-staged] [--force-non-staged]");
         pw.println("       [--staged-ready-timeout TIMEOUT] [--ignore-dexopt-profile]");
+        pw.println("       [--dexopt-compiler-filter FILTER]");
         pw.println("       [PATH [SPLIT...]|-]");
         pw.println("    Install an application.  Must provide the apk data to install, either as");
         pw.println("    file path(s) or '-' to read from stdin.  Options are:");
@@ -4762,13 +4792,19 @@
         pw.println("          milliseconds for pre-reboot verification to complete when");
         pw.println("          performing staged install. This flag is used to alter the waiting");
         pw.println("          time. You can skip the waiting time by specifying a TIMEOUT of '0'");
-        pw.println("      --ignore-dexopt-profile: If set, all profiles are ignored by dexopt");
+        pw.println("      --ignore-dexopt-profile: if set, all profiles are ignored by dexopt");
         pw.println("          during the installation, including the profile in the DM file and");
         pw.println("          the profile embedded in the APK file. If an invalid profile is");
         pw.println("          provided during installation, no warning will be reported by `adb");
         pw.println("          install`.");
         pw.println("          This option does not affect later dexopt operations (e.g.,");
         pw.println("          background dexopt and manual `pm compile` invocations).");
+        pw.println("      --dexopt-compiler-filter: the target compiler filter for dexopt during");
+        pw.println("          the installation. The filter actually used may be different.");
+        pw.println("          Valid values: one of the values documented in");
+        pw.println("          https://source.android.com/docs/core/runtime/configure"
+                + "#compiler_filters");
+        pw.println("          or 'skip'");
         pw.println("");
         pw.println("  install-existing [--user USER_ID|all|current]");
         pw.println("       [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
@@ -4907,7 +4943,10 @@
         pw.println("");
         pw.println("  get-signature-permission-allowlist PARTITION");
         pw.println("    Prints the signature permission allowlist for a partition.");
-        pw.println("    PARTITION is one of system, vendor, product and system-ext");
+        pw.println("    PARTITION is one of system, vendor, product, system-ext and apex");
+        pw.println("");
+        pw.println("  get-shared-uid-allowlist");
+        pw.println("    Prints the shared UID allowlist.");
         pw.println("");
         pw.println("  trim-caches DESIRED_FREE_SPACE [internal|UUID]");
         pw.println("    Trim cache files to reach the given free space.");
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 2081f73..0acadb1 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -16,7 +16,12 @@
 
 package com.android.server.pm;
 
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -25,6 +30,7 @@
 import android.app.ActivityManager;
 import android.app.admin.SecurityLog;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.Flags;
 import android.content.pm.PackageManager;
@@ -376,7 +382,30 @@
             mCallingUid = callingUid;
         }
 
-        public boolean isSameComponent(ActivityInfo activityInfo) {
+        public boolean isLauncherActivity(@NonNull Computer computer, @UserIdInt int userId) {
+            if (mIsForWholeApp) {
+                return false;
+            }
+            // Query the launcher activities with the package name.
+            final Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.addCategory(Intent.CATEGORY_LAUNCHER);
+            intent.setPackage(mPackageName);
+            List<ResolveInfo> launcherActivities = computer.queryIntentActivitiesInternal(
+                    intent, null,
+                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | GET_RESOLVED_FILTER
+                            | MATCH_DISABLED_COMPONENTS, SYSTEM_UID, userId);
+            final int launcherActivitiesSize =
+                    launcherActivities != null ? launcherActivities.size() : 0;
+            for (int i = 0; i < launcherActivitiesSize; i++) {
+                ResolveInfo resolveInfo = launcherActivities.get(i);
+                if (isSameComponent(resolveInfo.activityInfo)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private boolean isSameComponent(ActivityInfo activityInfo) {
             if (activityInfo == null) {
                 return false;
             }
@@ -395,25 +424,13 @@
             Slog.d(TAG, "Fail to report component state due to metrics is empty");
             return;
         }
-        boolean isLauncher = false;
-        final List<ResolveInfo> resolveInfosForLauncher = getHomeActivitiesResolveInfoAsUser(
-                computer, userId);
-        final int resolveInfosForLauncherSize =
-                resolveInfosForLauncher != null ? resolveInfosForLauncher.size() : 0;
         final int metricsSize = componentStateMetricsList.size();
         for (int i = 0; i < metricsSize; i++) {
             final ComponentStateMetrics componentStateMetrics = componentStateMetricsList.get(i);
-            for (int j = 0; j < resolveInfosForLauncherSize; j++) {
-                ResolveInfo resolveInfo = resolveInfosForLauncher.get(j);
-                if (componentStateMetrics.isSameComponent(resolveInfo.activityInfo)) {
-                    isLauncher = true;
-                    break;
-                }
-            }
             reportComponentStateChanged(componentStateMetrics.mUid,
                     componentStateMetrics.mComponentOldState,
                     componentStateMetrics.mComponentNewState,
-                    isLauncher,
+                    componentStateMetrics.isLauncherActivity(computer, userId),
                     componentStateMetrics.mIsForWholeApp,
                     componentStateMetrics.mCallingUid);
         }
@@ -424,10 +441,4 @@
         FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED,
                 uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid);
     }
-
-    private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer,
-            @UserIdInt int userId) {
-        return computer.queryIntentActivitiesInternal(computer.getHomeIntent(), /* resolvedType */
-                null, /* flags */ 0, userId);
-    }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 8d6d774..3956552 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -934,6 +934,7 @@
             ret.setTargetSdkVersion(p.getTargetSdkVersion());
             ret.setRestrictUpdateHash(p.getRestrictUpdateHash());
             ret.setScannedAsStoppedSystemApp(p.isScannedAsStoppedSystemApp());
+            ret.setInstallSource(p.getInstallSource());
         }
         mDisabledSysPackages.remove(name);
         return ret;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 78d8002..1cd77ff 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -320,10 +320,10 @@
 
     private final Handler mHandler;
 
-    @GuardedBy("itself")
+    @GuardedBy("mServiceLock")
     private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
 
-    @GuardedBy("itself")
+    @GuardedBy("mServiceLock")
     private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks =
             new ArrayList<>(1);
 
@@ -1847,9 +1847,7 @@
                         return;
                     }
 
-                    synchronized (mListeners) {
-                        copy = new ArrayList<>(mListeners);
-                    }
+                    copy = new ArrayList<>(mListeners);
                 }
                 // Note onShortcutChanged() needs to be called with the system service permissions.
                 for (int i = copy.size() - 1; i >= 0; i--) {
@@ -1874,9 +1872,8 @@
                     if (!isUserUnlockedL(userId)) {
                         return;
                     }
-                    synchronized (mShortcutChangeCallbacks) {
-                        copy = new ArrayList<>(mShortcutChangeCallbacks);
-                    }
+
+                    copy = new ArrayList<>(mShortcutChangeCallbacks);
                 }
                 for (int i = copy.size() - 1; i >= 0; i--) {
                     if (!CollectionUtils.isEmpty(changedList)) {
@@ -3432,7 +3429,7 @@
 
         @Override
         public void addListener(@NonNull ShortcutChangeListener listener) {
-            synchronized (mListeners) {
+            synchronized (mServiceLock) {
                 mListeners.add(Objects.requireNonNull(listener));
             }
         }
@@ -3440,7 +3437,7 @@
         @Override
         public void addShortcutChangeCallback(
                 @NonNull LauncherApps.ShortcutChangeCallback callback) {
-            synchronized (mShortcutChangeCallbacks) {
+            synchronized (mServiceLock) {
                 mShortcutChangeCallbacks.add(Objects.requireNonNull(callback));
             }
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 29acbcd..db94d0e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -281,6 +281,10 @@
     private static final String RESTRICTIONS_FILE_PREFIX = "res_";
     private static final String XML_SUFFIX = ".xml";
 
+    private static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY = "custom_logo_res_id";
+    private static final String CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY =
+            "custom_logo_description";
+
     private static final int ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION =
             UserInfo.FLAG_MANAGED_PROFILE
             | UserInfo.FLAG_PROFILE
@@ -1970,6 +1974,14 @@
         // intentSender
         unlockIntent.putExtra(Intent.EXTRA_INTENT, pendingIntent.getIntentSender());
         unlockIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+        if (Flags.enablePrivateSpaceFeatures() && Flags.usePrivateSpaceIconInBiometricPrompt()
+                && getUserInfo(userId).isPrivateProfile()) {
+            unlockIntent.putExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_RES_ID_KEY,
+                    com.android.internal.R.drawable.stat_sys_private_profile_status);
+            unlockIntent.putExtra(CUSTOM_BIOMETRIC_PROMPT_LOGO_DESCRIPTION_KEY,
+                    mContext.getString(R.string.private_space_biometric_prompt_title));
+        }
         mContext.startActivityAsUser(
                 unlockIntent, UserHandle.of(getProfileParentIdUnchecked(userId)));
     }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 483d308..95e5b84 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -156,7 +156,8 @@
             UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
             UserManager.DISALLOW_SIM_GLOBALLY,
             UserManager.DISALLOW_ASSIST_CONTENT,
-            UserManager.DISALLOW_THREAD_NETWORK
+            UserManager.DISALLOW_THREAD_NETWORK,
+            UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
     });
 
     public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
@@ -208,7 +209,8 @@
             UserManager.DISALLOW_CELLULAR_2G,
             UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
             UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
-            UserManager.DISALLOW_THREAD_NETWORK
+            UserManager.DISALLOW_THREAD_NETWORK,
+            UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
     );
 
     /**
@@ -254,7 +256,8 @@
                     UserManager.DISALLOW_CELLULAR_2G,
                     UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
                     UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
-                    UserManager.DISALLOW_THREAD_NETWORK
+                    UserManager.DISALLOW_THREAD_NETWORK,
+                    UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
             );
 
     /**
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index fcdc008..8cf248d 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -73,12 +73,6 @@
     // or device setup and should be scheduled appropriately.
     public static final int DEXOPT_FOR_RESTORE = 1 << 11; // TODO(b/135202722): remove
 
-    /**
-     * A value indicating that dexopt shouldn't be run.  This string is only used when loading
-     * filters from the `pm.dexopt.install*` properties and is not propagated to dex2oat.
-     */
-    public static final String COMPILER_FILTER_NOOP = "skip";
-
     // The name of package to optimize.
     private final String mPackageName;
 
@@ -186,10 +180,6 @@
         return mCompilationReason;
     }
 
-    public boolean isCompilationEnabled() {
-        return !mCompilerFilter.equals(COMPILER_FILTER_NOOP);
-    }
-
     /**
      * Creates a new set of DexoptOptions which are the same with the exception of the compiler
      * filter (set to the given value).
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index 3edd697..f518769 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -38,6 +38,7 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.HexConsumer;
 import com.android.internal.util.function.HexFunction;
 import com.android.internal.util.function.OctFunction;
 import com.android.internal.util.function.QuadFunction;
@@ -269,8 +270,8 @@
                 if (isDelegatePermission(permissionName)) {
                     final long identity = Binder.clearCallingIdentity();
                     try {
-                        return checkPermission(SHELL_PKG, permissionName,
-                                persistentDeviceId, userId, superImpl);
+                        return checkPermission(SHELL_PKG, permissionName, persistentDeviceId,
+                                userId, superImpl);
                     } finally {
                         Binder.restoreCallingIdentity(identity);
                     }
@@ -323,8 +324,7 @@
                         Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(code, shellUid, "com.android.shell", null,
-                            virtualDeviceId, raw);
+                    return superImpl.apply(code, shellUid, SHELL_PKG, null, virtualDeviceId, raw);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
@@ -340,7 +340,7 @@
                         Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(code, usage, shellUid, "com.android.shell");
+                    return superImpl.apply(code, usage, shellUid, SHELL_PKG);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
@@ -359,9 +359,8 @@
                         Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(code, shellUid, "com.android.shell", featureId,
-                            virtualDeviceId, shouldCollectAsyncNotedOp, message,
-                            shouldCollectMessage);
+                    return superImpl.apply(code, shellUid, SHELL_PKG, featureId, virtualDeviceId,
+                            shouldCollectAsyncNotedOp, message, shouldCollectMessage);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
@@ -382,8 +381,8 @@
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     return superImpl.apply(code,
-                            new AttributionSource(shellUid, Process.INVALID_PID,
-                                    "com.android.shell", attributionSource.getAttributionTag(),
+                            new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+                                    attributionSource.getAttributionTag(),
                                     attributionSource.getToken(), /*renouncedPermissions*/ null,
                                     attributionSource.getDeviceId(), attributionSource.getNext()),
                             shouldCollectAsyncNotedOp, message, shouldCollectMessage,
@@ -409,10 +408,9 @@
                         Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(token, code, shellUid, "com.android.shell",
-                            attributionTag, virtualDeviceId, startIfModeDefault,
-                            shouldCollectAsyncNotedOp, message, shouldCollectMessage,
-                            attributionFlags, attributionChainId);
+                    return superImpl.apply(token, code, shellUid, SHELL_PKG, attributionTag,
+                            virtualDeviceId, startIfModeDefault, shouldCollectAsyncNotedOp, message,
+                            shouldCollectMessage, attributionFlags, attributionChainId);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
@@ -438,8 +436,8 @@
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     return superImpl.apply(clientId, code,
-                            new AttributionSource(shellUid, Process.INVALID_PID,
-                                    "com.android.shell", attributionSource.getAttributionTag(),
+                            new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+                                    attributionSource.getAttributionTag(),
                                     attributionSource.getToken(), /*renouncedPermissions*/ null,
                                     attributionSource.getDeviceId(), attributionSource.getNext()),
                             startIfModeDefault, shouldCollectAsyncNotedOp, message,
@@ -465,11 +463,12 @@
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     superImpl.apply(clientId, code,
-                            new AttributionSource(shellUid, Process.INVALID_PID,
-                                    "com.android.shell", attributionSource.getAttributionTag(),
+                            new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+                                    attributionSource.getAttributionTag(),
                                     attributionSource.getToken(), /*renouncedPermissions*/ null,
                                     attributionSource.getDeviceId(), attributionSource.getNext()),
                             skipProxyOperation);
+                    return;
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
@@ -477,6 +476,26 @@
             superImpl.apply(clientId, code, attributionSource, skipProxyOperation);
         }
 
+        @Override
+        public void finishOperation(IBinder clientId, int code, int uid, String packageName,
+                String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
+                                        Integer, String, String, Integer> superImpl) {
+            if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+                final int shellUid =
+                        UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID);
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    superImpl.accept(clientId, code, shellUid, SHELL_PKG, attributionTag,
+                            virtualDeviceId);
+                    return;
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            superImpl.accept(clientId, code, uid, packageName, attributionTag,
+                    virtualDeviceId);
+        }
+
         private boolean isDelegatePermission(@NonNull String permission) {
             // null permissions means all permissions are delegated
             return mDelegateAndOwnerUid != INVALID_UID
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
index 23872d4f..119b659 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
@@ -336,8 +336,13 @@
             final PermissionManagerServiceInternal permissionManagerInternal =
                     LocalServices.getService(PermissionManagerServiceInternal.class);
             for (final int userId : UserManagerService.getInstance().getUserIds()) {
-                packageManagerInternal.forEachPackage(pkg ->
-                        permissionManagerInternal.resetRuntimePermissions(pkg, userId));
+                packageManagerInternal.forEachPackage(pkg -> {
+                    // Filter out packages that don't have app IDs which means they don't have
+                    // permission states either.
+                    if (pkg.getUid() != -1) {
+                        permissionManagerInternal.resetRuntimePermissions(pkg, userId);
+                    }
+                });
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java b/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java
index d138606..6b99cbb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionAllowlist.java
@@ -55,6 +55,9 @@
     @NonNull
     private final ArrayMap<String, ArrayMap<String, Boolean>> mSystemExtSignatureAppAllowlist =
             new ArrayMap<>();
+    @NonNull
+    private final ArrayMap<String, ArrayMap<String, Boolean>> mApexSignatureAppAllowlist =
+            new ArrayMap<>();
 
     @NonNull
     public ArrayMap<String, ArrayMap<String, Boolean>> getOemAppAllowlist() {
@@ -107,6 +110,11 @@
         return mSystemExtSignatureAppAllowlist;
     }
 
+    @NonNull
+    public ArrayMap<String, ArrayMap<String, Boolean>> getApexSignatureAppAllowlist() {
+        return mApexSignatureAppAllowlist;
+    }
+
     @Nullable
     public Boolean getOemAppAllowlistState(@NonNull String packageName,
             @NonNull String permissionName) {
@@ -211,4 +219,14 @@
         }
         return permissions.get(permissionName);
     }
+
+    @Nullable
+    public Boolean getApexSignatureAppAllowlistState(@NonNull String packageName,
+            @NonNull String permissionName) {
+        ArrayMap<String, Boolean> permissions = mApexSignatureAppAllowlist.get(packageName);
+        if (permissions == null) {
+            return null;
+        }
+        return permissions.get(permissionName);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index cd1d799..ea71953 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -165,6 +165,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -368,18 +369,32 @@
                 return false;
             }
 
+            int userId = UserHandle.getUserId(uid);
+
+            boolean isInSetup =
+                    getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId)
+                             .map(setupState -> setupState == 0)
+                             .orElse(false);
+            if (isInSetup) {
+                return true;
+            }
+
+            boolean isInDeferredSetup =
+                    getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId)
+                            .map(state ->
+                                    state == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED)
+                            .orElse(false);
+            return isInDeferredSetup;
+        }
+
+        private Optional<Integer> getSecureInt(String settingName, int userId) {
             try {
-                int userId = UserHandle.getUserId(uid);
-                boolean isInSetup = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.USER_SETUP_COMPLETE, userId) == 0;
-                boolean isInDeferredSetup = Settings.Secure.getIntForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId)
-                        == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED;
-                return isInSetup || isInDeferredSetup;
+                return Optional.of(
+                        Settings.Secure.getIntForUser(
+                                mContext.getContentResolver(), settingName, userId));
             } catch (Settings.SettingNotFoundException e) {
-                Slog.w(LOG_TAG, "Failed to check if the user is in restore: " + e);
-                return false;
+                Slog.i(LOG_TAG, "Setting " + settingName + " not found", e);
+                return Optional.empty();
             }
         }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 12e5180..bf90a6c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -486,7 +486,7 @@
 
     boolean mBootMessageNeedsHiding;
     volatile boolean mBootAnimationDismissable;
-    private KeyguardServiceDelegate mKeyguardDelegate;
+    @VisibleForTesting KeyguardServiceDelegate mKeyguardDelegate;
     private boolean mKeyguardBound;
     final DrawnListener mKeyguardDrawnCallback = new DrawnListener() {
         @Override
@@ -2882,6 +2882,8 @@
         }
         ContentResolver resolver = mContext.getContentResolver();
         boolean updateRotation = false;
+        boolean updateKidsModeSettings = false;
+        final boolean kidsModeEnabled;
         synchronized (mLock) {
             mEndcallBehavior = Settings.System.getIntForUser(resolver,
                     Settings.System.END_BUTTON_BEHAVIOR,
@@ -2995,20 +2997,23 @@
                     Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1;
             mInputManagerInternal.setStylusButtonMotionEventsEnabled(mStylusButtonsEnabled);
 
-            final boolean kidsModeEnabled = Settings.Secure.getIntForUser(resolver,
+            kidsModeEnabled = Settings.Secure.getIntForUser(resolver,
                     Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1;
             if (mKidsModeEnabled != kidsModeEnabled) {
                 mKidsModeEnabled = kidsModeEnabled;
-                updateKidsModeSettings();
+                updateKidsModeSettings = true;
             }
         }
+        if (updateKidsModeSettings) {
+            updateKidsModeSettings(kidsModeEnabled);
+        }
         if (updateRotation) {
             updateRotation(true);
         }
     }
 
-    private void updateKidsModeSettings() {
-        if (mKidsModeEnabled) {
+    private void updateKidsModeSettings(boolean kidsModeEnabled) {
+        if (kidsModeEnabled) {
             // Needed since many Kids apps aren't optimised to support both orientations and it
             // will be hard for kids to understand the app compat mode.
             // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once
@@ -6511,6 +6516,9 @@
     @Override
     public void setSwitchingUser(boolean switching) {
         mKeyguardDelegate.setSwitchingUser(switching);
+        if (switching) {
+            dismissKeyboardShortcutsMenu();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
index b05a421..e544ae6 100644
--- a/services/core/java/com/android/server/policy/TalkbackShortcutController.java
+++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
@@ -117,6 +117,7 @@
     }
 
     private boolean isTalkback(ServiceInfo info) {
-        return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString());
+        return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString())
+            && (info.applicationInfo.isSystemApp() || info.applicationInfo.isUpdatedSystemApp());
     }
 }
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index f5882a3..7f24769 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -74,7 +74,6 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -544,7 +543,7 @@
             if (!mHalReady.get()) {
                 FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, getCallingUid(),
                             FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__HAL_NOT_READY,
-                            Float.NaN);
+                            Float.NaN, forecastSeconds);
                 return Float.NaN;
             }
 
@@ -554,7 +553,7 @@
                 }
                 FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, getCallingUid(),
                             FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__INVALID_ARGUMENT,
-                            Float.NaN);
+                            Float.NaN, forecastSeconds);
                 return Float.NaN;
             }
 
@@ -1610,7 +1609,7 @@
         /** Map of skin temperature sensor name to a corresponding list of samples */
         @GuardedBy("mSamples")
         @VisibleForTesting
-        final ArrayMap<String, LinkedList<Sample>> mSamples = new ArrayMap<>();
+        final ArrayMap<String, ArrayList<Sample>> mSamples = new ArrayMap<>();
 
         /** Map of skin temperature sensor name to the corresponding SEVERE temperature threshold */
         @GuardedBy("mSamples")
@@ -1707,8 +1706,8 @@
                         continue;
                     }
 
-                    LinkedList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
-                            k -> new LinkedList<>());
+                    ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
+                            k -> new ArrayList<>(RING_BUFFER_SIZE));
                     if (samples.size() == RING_BUFFER_SIZE) {
                         samples.removeFirst();
                     }
@@ -1725,7 +1724,8 @@
         float getSlopeOf(List<Sample> samples) {
             long sumTimes = 0L;
             float sumTemperatures = 0.0f;
-            for (final Sample sample : samples) {
+            for (int s = 0; s < samples.size(); ++s) {
+                Sample sample = samples.get(s);
                 sumTimes += sample.time;
                 sumTemperatures += sample.temperature;
             }
@@ -1734,7 +1734,8 @@
 
             long sampleVariance = 0L;
             float sampleCovariance = 0.0f;
-            for (final Sample sample : samples) {
+            for (int s = 0; s < samples.size(); ++s) {
+                Sample sample = samples.get(s);
                 long timeDelta = sample.time - meanTime;
                 float temperatureDelta = sample.temperature - meanTemperature;
                 sampleVariance += timeDelta * timeDelta;
@@ -1777,7 +1778,7 @@
                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
                             Binder.getCallingUid(),
                             FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE,
-                            Float.NaN);
+                            Float.NaN, forecastSeconds);
                     return Float.NaN;
                 }
 
@@ -1788,15 +1789,15 @@
                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
                             Binder.getCallingUid(),
                             THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD,
-                            Float.NaN);
+                            Float.NaN, forecastSeconds);
                     return Float.NaN;
                 }
 
                 float maxNormalized = Float.NaN;
                 int noThresholdSampleCount = 0;
-                for (Map.Entry<String, LinkedList<Sample>> entry : mSamples.entrySet()) {
+                for (Map.Entry<String, ArrayList<Sample>> entry : mSamples.entrySet()) {
                     String name = entry.getKey();
-                    LinkedList<Sample> samples = entry.getValue();
+                    ArrayList<Sample> samples = entry.getValue();
 
                     Float threshold = mSevereThresholds.get(name);
                     if (threshold == null) {
@@ -1827,12 +1828,12 @@
                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
                             Binder.getCallingUid(),
                             THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD,
-                            Float.NaN);
+                            Float.NaN, forecastSeconds);
                 } else {
                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
                             Binder.getCallingUid(),
                             FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
-                            maxNormalized);
+                            maxNormalized, forecastSeconds);
                 }
                 return maxNormalized;
             }
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index df502eb..7e27407 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -19,6 +19,7 @@
 import static android.os.Flags.adpfUseFmqChannel;
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import static com.android.server.power.hint.Flags.adpfSessionTag;
 import static com.android.server.power.hint.Flags.powerhintThreadCleanup;
 
 import android.annotation.NonNull;
@@ -28,6 +29,8 @@
 import android.app.StatsManager;
 import android.app.UidObserver;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.hardware.power.ChannelConfig;
 import android.hardware.power.IPower;
 import android.hardware.power.SessionConfig;
@@ -130,6 +133,7 @@
 
     private final IPower mPowerHal;
     private int mPowerHalVersion;
+    private final PackageManager mPackageManager;
 
     private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint";
     private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager";
@@ -151,6 +155,11 @@
             mCleanUpHandler = null;
             mNonIsolatedTids = null;
         }
+        if (adpfSessionTag()) {
+            mPackageManager = mContext.getPackageManager();
+        } else {
+            mPackageManager = null;
+        }
         mActiveSessions = new ArrayMap<>();
         mChannelMap = new ArrayMap<>();
         mNativeWrapper = injector.createNativeWrapper();
@@ -402,7 +411,7 @@
             FgThread.getHandler().post(() -> {
                 synchronized (mLock) {
                     boolean shouldCleanup = false;
-                    if (powerhintThreadCleanup()) {
+                    if (mPowerHalVersion >= 4 && powerhintThreadCleanup()) {
                         int prevProcState = mProcStatesCache.get(uid, Integer.MAX_VALUE);
                         shouldCleanup =
                                 prevProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
@@ -819,6 +828,25 @@
                     throw new SecurityException(errMsg);
                 }
 
+                if (adpfSessionTag() && tag == SessionTag.APP) {
+                    // If the category of the app is a game,
+                    // we change the session tag to SessionTag.GAME
+                    // as it was not previously classified
+                    switch (getUidApplicationCategory(callingUid)) {
+                        case ApplicationInfo.CATEGORY_GAME:
+                            tag = SessionTag.GAME;
+                            break;
+                        case ApplicationInfo.CATEGORY_UNDEFINED:
+                            // We use CATEGORY_UNDEFINED to filter the case when
+                            // PackageManager.NameNotFoundException is caught,
+                            // which should not happen.
+                            tag = SessionTag.APP;
+                            break;
+                        default:
+                            tag = SessionTag.APP;
+                    }
+                }
+
                 Long halSessionPtr = null;
                 if (mConfigCreationSupport.get()) {
                     try {
@@ -857,7 +885,10 @@
                     }
                 }
 
-                logPerformanceHintSessionAtom(callingUid, halSessionPtr, durationNanos, tids);
+                final long sessionId = config != null ? config.id : halSessionPtr;
+                logPerformanceHintSessionAtom(
+                        callingUid, sessionId, durationNanos, tids, tag);
+
                 synchronized (mLock) {
                     AppHintSession hs = new AppHintSession(callingUid, callingTgid, tids, token,
                             halSessionPtr, durationNanos);
@@ -944,9 +975,20 @@
         }
 
         private void logPerformanceHintSessionAtom(int uid, long sessionId,
-                long targetDuration, int[] tids) {
+                long targetDuration, int[] tids, @SessionTag int sessionTag) {
             FrameworkStatsLog.write(FrameworkStatsLog.PERFORMANCE_HINT_SESSION_REPORTED, uid,
-                    sessionId, targetDuration, tids.length);
+                    sessionId, targetDuration, tids.length, sessionTag);
+        }
+
+        private int getUidApplicationCategory(int uid) {
+            try {
+                final String packageName = mPackageManager.getNameForUid(uid);
+                final ApplicationInfo applicationInfo =
+                        mPackageManager.getApplicationInfo(packageName, PackageManager.MATCH_ALL);
+                return applicationInfo.category;
+            } catch (PackageManager.NameNotFoundException e) {
+                return ApplicationInfo.CATEGORY_UNDEFINED;
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/power/hint/TEST_MAPPING b/services/core/java/com/android/server/power/hint/TEST_MAPPING
index ce6277d..34c25c6 100644
--- a/services/core/java/com/android/server/power/hint/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/hint/TEST_MAPPING
@@ -10,16 +10,18 @@
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
-    },
+    }
+  ],
+  "postsubmit": [
     {
       "name": "CtsStatsdAtomHostTestCases",
       "options": [
         {"exclude-annotation": "androidx.test.filters.FlakyTest"},
         {"exclude-annotation": "org.junit.Ignore"},
-        {"include-filter": "android.cts.statsdatom.powermanager"}
+        {"include-filter": "android.cts.statsdatom.performancehintmanager"}
       ],
       "file_patterns": [
-        "(/|^)ThermalManagerService.java"
+        "(/|^)HintManagerService.java"
       ]
     }
   ]
diff --git a/services/core/java/com/android/server/power/hint/flags.aconfig b/services/core/java/com/android/server/power/hint/flags.aconfig
index 0997744..55afa05 100644
--- a/services/core/java/com/android/server/power/hint/flags.aconfig
+++ b/services/core/java/com/android/server/power/hint/flags.aconfig
@@ -7,3 +7,10 @@
     description: "Feature flag for auto PowerHintSession dead thread cleanup"
     bug: "296160319"
 }
+
+flag {
+    name: "adpf_session_tag"
+    namespace: "game"
+    description: "Feature flag for adding session tag to hint session atom"
+    bug: "345011125"
+}
diff --git a/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java
new file mode 100644
index 0000000..a48f162
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.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.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+
+public class AudioPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    public AudioPowerStatsProcessor(PowerProfile powerProfile,
+            PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_AUDIO, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_AUDIO));
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states & BatteryStats.HistoryItem.STATE_AUDIO_ON_FLAG) != 0
+                ? STATE_ON
+                : STATE_OFF;
+    }
+}
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 efaa7a8..322ed86 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -298,6 +298,8 @@
     private final MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector;
     private final WifiPowerStatsCollector mWifiPowerStatsCollector;
     private final BluetoothPowerStatsCollector mBluetoothPowerStatsCollector;
+    private final CameraPowerStatsCollector mCameraPowerStatsCollector;
+    private final GnssPowerStatsCollector mGnssPowerStatsCollector;
     private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
     private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever =
             new WifiPowerStatsCollector.WifiStatsRetriever() {
@@ -1963,7 +1965,7 @@
 
     private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector,
             MobileRadioPowerStatsCollector.Injector, WifiPowerStatsCollector.Injector,
-            BluetoothPowerStatsCollector.Injector {
+            BluetoothPowerStatsCollector.Injector, EnergyConsumerPowerStatsCollector.Injector {
         private PackageManager mPackageManager;
         private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
         private NetworkStatsManager mNetworkStatsManager;
@@ -5446,7 +5448,10 @@
         final int mappedUid = mapUid(uid);
         if (mGpsNesting == 0) {
             mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE_GPS_ON_FLAG);
+                    HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss");
+            if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+                mGnssPowerStatsCollector.schedule();
+            }
         }
         mGpsNesting++;
 
@@ -5465,11 +5470,14 @@
         mGpsNesting--;
         if (mGpsNesting == 0) {
             mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE_GPS_ON_FLAG);
+                    HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss");
             mHistory.recordGpsSignalQualityEvent(elapsedRealtimeMs, uptimeMs,
                     GPS_SIGNAL_QUALITY_NONE);
             stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
             mGpsSignalQualityBin = -1;
+            if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+                mGnssPowerStatsCollector.schedule();
+            }
         }
 
         mFrameworkStatsLogger.gpsScanStateChanged(mapIsolatedUid(uid), workChain, /* on */ false);
@@ -6490,12 +6498,14 @@
         uid = mapUid(uid);
         if (mAudioOnNesting == 0) {
             mHistory.recordStateStartEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE_AUDIO_ON_FLAG);
+                    HistoryItem.STATE_AUDIO_ON_FLAG, uid, "audio");
             mAudioOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
         mAudioOnNesting++;
-        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
-                .noteAudioTurnedOnLocked(elapsedRealtimeMs);
+        if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteAudioTurnedOnLocked(elapsedRealtimeMs);
+        }
     }
 
     @GuardedBy("this")
@@ -6506,11 +6516,13 @@
         uid = mapUid(uid);
         if (--mAudioOnNesting == 0) {
             mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE_AUDIO_ON_FLAG);
+                    HistoryItem.STATE_AUDIO_ON_FLAG, uid, "audio");
             mAudioOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
-                .noteAudioTurnedOffLocked(elapsedRealtimeMs);
+        if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteAudioTurnedOffLocked(elapsedRealtimeMs);
+        }
     }
 
     @GuardedBy("this")
@@ -6518,12 +6530,14 @@
         uid = mapUid(uid);
         if (mVideoOnNesting == 0) {
             mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE2_VIDEO_ON_FLAG);
+                    HistoryItem.STATE2_VIDEO_ON_FLAG, uid, "video");
             mVideoOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
         mVideoOnNesting++;
-        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
-                .noteVideoTurnedOnLocked(elapsedRealtimeMs);
+        if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteVideoTurnedOnLocked(elapsedRealtimeMs);
+        }
     }
 
     @GuardedBy("this")
@@ -6534,11 +6548,13 @@
         uid = mapUid(uid);
         if (--mVideoOnNesting == 0) {
             mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE2_VIDEO_ON_FLAG);
+                    HistoryItem.STATE2_VIDEO_ON_FLAG, uid, "video");
             mVideoOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
-                .noteVideoTurnedOffLocked(elapsedRealtimeMs);
+        if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteVideoTurnedOffLocked(elapsedRealtimeMs);
+        }
     }
 
     @GuardedBy("this")
@@ -6613,11 +6629,13 @@
         uid = mapUid(uid);
         if (mFlashlightOnNesting++ == 0) {
             mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE2_FLASHLIGHT_FLAG);
+                    HistoryItem.STATE2_FLASHLIGHT_FLAG, uid, "flashlight");
             mFlashlightOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
-                .noteFlashlightTurnedOnLocked(elapsedRealtimeMs);
+        if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteFlashlightTurnedOnLocked(elapsedRealtimeMs);
+        }
     }
 
     @GuardedBy("this")
@@ -6628,11 +6646,13 @@
         uid = mapUid(uid);
         if (--mFlashlightOnNesting == 0) {
             mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE2_FLASHLIGHT_FLAG);
+                    HistoryItem.STATE2_FLASHLIGHT_FLAG, uid, "flashlight");
             mFlashlightOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
-        getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
-                .noteFlashlightTurnedOffLocked(elapsedRealtimeMs);
+        if (!mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
+            getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                    .noteFlashlightTurnedOffLocked(elapsedRealtimeMs);
+        }
     }
 
     @GuardedBy("this")
@@ -6640,13 +6660,17 @@
         uid = mapUid(uid);
         if (mCameraOnNesting++ == 0) {
             mHistory.recordState2StartEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE2_CAMERA_FLAG);
+                    HistoryItem.STATE2_CAMERA_FLAG, uid, "camera");
             mCameraOnTimer.startRunningLocked(elapsedRealtimeMs);
         }
         getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
                 .noteCameraTurnedOnLocked(elapsedRealtimeMs);
 
-        scheduleSyncExternalStatsLocked("camera-on", ExternalStatsSync.UPDATE_CAMERA);
+        if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
+            mCameraPowerStatsCollector.schedule();
+        } else {
+            scheduleSyncExternalStatsLocked("camera-on", ExternalStatsSync.UPDATE_CAMERA);
+        }
     }
 
     @GuardedBy("this")
@@ -6657,13 +6681,17 @@
         uid = mapUid(uid);
         if (--mCameraOnNesting == 0) {
             mHistory.recordState2StopEvent(elapsedRealtimeMs, uptimeMs,
-                    HistoryItem.STATE2_CAMERA_FLAG);
+                    HistoryItem.STATE2_CAMERA_FLAG, uid, "camera");
             mCameraOnTimer.stopRunningLocked(elapsedRealtimeMs);
         }
         getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
                 .noteCameraTurnedOffLocked(elapsedRealtimeMs);
 
-        scheduleSyncExternalStatsLocked("camera-off", ExternalStatsSync.UPDATE_CAMERA);
+        if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
+            mCameraPowerStatsCollector.schedule();
+        } else {
+            scheduleSyncExternalStatsLocked("camera-off", ExternalStatsSync.UPDATE_CAMERA);
+        }
     }
 
     @GuardedBy("this")
@@ -11269,6 +11297,12 @@
                 mPowerStatsCollectorInjector);
         mBluetoothPowerStatsCollector.addConsumer(this::recordPowerStats);
 
+        mCameraPowerStatsCollector = new CameraPowerStatsCollector(mPowerStatsCollectorInjector);
+        mCameraPowerStatsCollector.addConsumer(this::recordPowerStats);
+
+        mGnssPowerStatsCollector = new GnssPowerStatsCollector(mPowerStatsCollectorInjector);
+        mGnssPowerStatsCollector.addConsumer(this::recordPowerStats);
+
         mStartCount++;
         initTimersAndCounters();
         mOnBattery = mOnBatteryInternal = false;
@@ -14691,6 +14725,14 @@
                 mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH));
         mBluetoothPowerStatsCollector.schedule();
 
+        mCameraPowerStatsCollector.setEnabled(
+                mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA));
+        mCameraPowerStatsCollector.schedule();
+
+        mGnssPowerStatsCollector.setEnabled(
+                mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS));
+        mGnssPowerStatsCollector.schedule();
+
         mSystemReady = true;
     }
 
@@ -14709,6 +14751,10 @@
                 return mWifiPowerStatsCollector;
             case BatteryConsumer.POWER_COMPONENT_BLUETOOTH:
                 return mBluetoothPowerStatsCollector;
+            case BatteryConsumer.POWER_COMPONENT_CAMERA:
+                return mCameraPowerStatsCollector;
+            case BatteryConsumer.POWER_COMPONENT_GNSS:
+                return mGnssPowerStatsCollector;
         }
         return null;
     }
@@ -16246,6 +16292,8 @@
         mMobileRadioPowerStatsCollector.forceSchedule();
         mWifiPowerStatsCollector.forceSchedule();
         mBluetoothPowerStatsCollector.forceSchedule();
+        mCameraPowerStatsCollector.forceSchedule();
+        mGnssPowerStatsCollector.forceSchedule();
     }
 
     /**
@@ -16266,6 +16314,8 @@
         mMobileRadioPowerStatsCollector.collectAndDump(pw);
         mWifiPowerStatsCollector.collectAndDump(pw);
         mBluetoothPowerStatsCollector.collectAndDump(pw);
+        mCameraPowerStatsCollector.collectAndDump(pw);
+        mGnssPowerStatsCollector.collectAndDump(pw);
     }
 
     private final Runnable mWriteAsyncRunnable = () -> {
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 b252395..ce0ee39 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -95,11 +95,21 @@
                 }
                 mPowerCalculators.add(new SensorPowerCalculator(
                         mContext.getSystemService(SensorManager.class)));
-                mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
-                mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
-                mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
-                mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
-                mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
+                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+                    mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
+                }
+                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
+                    mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
+                }
+                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
+                    mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
+                }
+                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
+                    mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
+                }
+                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
+                    mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
+                }
                 mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
index 490bd5e..599e63d 100644
--- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
@@ -51,7 +51,7 @@
     private long mLastUpdateTimestamp;
 
     private PowerStats.Descriptor mDescriptor;
-    private final BinaryStatePowerStatsLayout mStatsLayout = new BinaryStatePowerStatsLayout();
+    private final BinaryStatePowerStatsLayout mStatsLayout;
     private PowerStats mPowerStats;
     private PowerEstimationPlan mPlan;
     private long[] mTmpDeviceStatsArray;
@@ -59,9 +59,17 @@
 
     BinaryStatePowerStatsProcessor(int powerComponentId,
             PowerStatsUidResolver uidResolver, double averagePowerMilliAmp) {
+        this(powerComponentId, uidResolver, averagePowerMilliAmp,
+                new BinaryStatePowerStatsLayout());
+    }
+
+    BinaryStatePowerStatsProcessor(int powerComponentId,
+            PowerStatsUidResolver uidResolver, double averagePowerMilliAmp,
+            BinaryStatePowerStatsLayout statsLayout) {
         mPowerComponentId = powerComponentId;
         mUsageBasedPowerEstimator = new UsageBasedPowerEstimator(averagePowerMilliAmp);
         mUidResolver = uidResolver;
+        mStatsLayout = statsLayout;
     }
 
     protected abstract @BinaryState int getBinaryState(BatteryStats.HistoryItem item);
@@ -107,7 +115,7 @@
                 mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid);
             }
         } else {
-            recordUsageDuration(item.time);
+            recordUsageDuration(mPowerStats, mInitiatingUid, item.time);
             mInitiatingUid = Process.INVALID_UID;
             if (!mEnergyConsumerSupported) {
                 flushPowerStats(stats, item.time);
@@ -117,20 +125,16 @@
         mLastState = state;
     }
 
-    private void recordUsageDuration(long time) {
-        if (mLastState == STATE_OFF) {
-            return;
-        }
-
+    protected void recordUsageDuration(PowerStats powerStats, int uid, long time) {
         long durationMs = time - mLastStateTimestamp;
         mStatsLayout.setUsageDuration(mPowerStats.stats,
                 mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs);
 
-        if (mInitiatingUid != Process.INVALID_UID) {
-            long[] uidStats = mPowerStats.uidStats.get(mInitiatingUid);
+        if (uid != Process.INVALID_UID) {
+            long[] uidStats = mPowerStats.uidStats.get(uid);
             if (uidStats == null) {
                 uidStats = new long[mDescriptor.uidStatsArrayLength];
-                mPowerStats.uidStats.put(mInitiatingUid, uidStats);
+                mPowerStats.uidStats.put(uid, uidStats);
                 mStatsLayout.setUidUsageDuration(uidStats, durationMs);
             } else {
                 mStatsLayout.setUsageDuration(mPowerStats.stats,
@@ -143,7 +147,11 @@
     void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats,
             long timestampMs) {
         ensureInitialized();
-        recordUsageDuration(timestampMs);
+
+        if (mLastState == STATE_ON) {
+            recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs);
+        }
+
         long consumedEnergy = mStatsLayout.getConsumedEnergy(powerStats.stats, 0);
         if (consumedEnergy != BatteryStats.POWER_DATA_UNAVAILABLE) {
             mEnergyConsumerSupported = true;
@@ -169,14 +177,16 @@
 
     @Override
     void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        recordUsageDuration(timestampMs);
+        if (mLastState == STATE_ON) {
+            recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs);
+        }
         flushPowerStats(stats, timestampMs);
 
         if (mPlan == null) {
             mPlan = new PowerEstimationPlan(stats.getConfig());
         }
 
-        computeDevicePowerEstimates(stats);
+        computeDevicePowerEstimates(stats, mPlan, mEnergyConsumerSupported);
         combineDevicePowerEstimates(stats);
 
         List<Integer> uids = new ArrayList<>();
@@ -186,9 +196,10 @@
         computeUidPowerEstimates(stats, uids);
     }
 
-    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+    protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            PowerEstimationPlan plan, boolean energyConsumerSupported) {
+        for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i);
             if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
                 continue;
             }
@@ -196,7 +207,7 @@
             long duration = mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
             if (duration > 0) {
                 double power;
-                if (mEnergyConsumerSupported) {
+                if (energyConsumerSupported) {
                     power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
                 } else {
                     power = mUsageBasedPowerEstimator.calculatePower(duration);
diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
new file mode 100644
index 0000000..8705bd5
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.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.server.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+
+public class CameraPowerStatsCollector extends EnergyConsumerPowerStatsCollector {
+
+    CameraPowerStatsCollector(Injector injector) {
+        super(injector, BatteryConsumer.POWER_COMPONENT_CAMERA,
+                BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_CAMERA),
+                EnergyConsumerType.CAMERA, /* energy consumer name */ null,
+                new BinaryStatePowerStatsLayout());
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java
new file mode 100644
index 0000000..15c3eb8
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.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.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+
+public class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    public CameraPowerStatsProcessor(PowerProfile powerProfile,
+            PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_CAMERA, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_CAMERA));
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states2 & BatteryStats.HistoryItem.STATE2_CAMERA_FLAG) != 0
+                ? STATE_ON
+                : STATE_OFF;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
new file mode 100644
index 0000000..2021f85
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.util.Slog;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+
+import java.util.function.IntSupplier;
+
+public class EnergyConsumerPowerStatsCollector extends PowerStatsCollector {
+    private static final String TAG = "CameraPowerStatsCollector";
+
+    private static final long CAMERA_ACTIVITY_REQUEST_TIMEOUT = 20000;
+
+    private static final long ENERGY_UNSPECIFIED = -1;
+
+    interface Injector {
+        Handler getHandler();
+        Clock getClock();
+        PowerStatsUidResolver getUidResolver();
+        long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
+        ConsumedEnergyRetriever getConsumedEnergyRetriever();
+        IntSupplier getVoltageSupplier();
+    }
+
+    private final Injector mInjector;
+    private final int mPowerComponentId;
+    private final String mPowerComponentName;
+    private final int mEnergyConsumerType;
+    private final String mEnergyConsumerName;
+
+    private final BinaryStatePowerStatsLayout mLayout;
+    private boolean mIsInitialized;
+
+    private PowerStats mPowerStats;
+    private ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    private IntSupplier mVoltageSupplier;
+    private int[] mEnergyConsumerIds = new int[0];
+    private long mLastConsumedEnergyUws = ENERGY_UNSPECIFIED;
+    private int mLastVoltageMv;
+    private long mLastUpdateTimestamp;
+    private boolean mFirstCollection = true;
+
+    EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId,
+            String powerComponentName, @EnergyConsumerType int energyConsumerType,
+            String energyConsumerName, BinaryStatePowerStatsLayout statsLayout) {
+        super(injector.getHandler(),
+                injector.getPowerStatsCollectionThrottlePeriod(powerComponentName),
+                injector.getUidResolver(), injector.getClock());
+        mInjector = injector;
+        mPowerComponentId = powerComponentId;
+        mPowerComponentName = powerComponentName;
+        mEnergyConsumerType = energyConsumerType;
+        mEnergyConsumerName = energyConsumerName;
+        mLayout = statsLayout;
+    }
+
+    private boolean ensureInitialized() {
+        if (mIsInitialized) {
+            return true;
+        }
+
+        if (!isEnabled()) {
+            return false;
+        }
+
+        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+        mVoltageSupplier = mInjector.getVoltageSupplier();
+        mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(mEnergyConsumerType,
+                mEnergyConsumerName);
+
+        PersistableBundle extras = new PersistableBundle();
+        mLayout.toExtras(extras);
+        PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
+                mPowerComponentId, mPowerComponentName, mLayout.getDeviceStatsArrayLength(),
+                null, 0, mLayout.getUidStatsArrayLength(),
+                extras);
+        mPowerStats = new PowerStats(powerStatsDescriptor);
+
+        mIsInitialized = true;
+        return true;
+    }
+
+    @Override
+    protected PowerStats collectStats() {
+        if (!ensureInitialized()) {
+            return null;
+        }
+
+        if (mEnergyConsumerIds.length == 0) {
+            return null;
+        }
+
+        long consumedEnergy = 0;
+        int voltageMv = mVoltageSupplier.getAsInt();
+        if (voltageMv <= 0) {
+            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+                    + " mV) when querying energy consumers");
+        } else {
+            long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
+            if (energyUws != null) {
+                for (int i = energyUws.length - 1; i >= 0; i--) {
+                    if (energyUws[i] != ENERGY_UNSPECIFIED) {
+                        consumedEnergy += energyUws[i];
+                    }
+                }
+            }
+        }
+
+        long energyDelta = mLastConsumedEnergyUws != ENERGY_UNSPECIFIED
+                ? consumedEnergy - mLastConsumedEnergyUws : 0;
+        mLastConsumedEnergyUws = consumedEnergy;
+        if (energyDelta < 0) {
+            // Likely, restart of powerstats HAL
+            energyDelta = 0;
+        }
+
+        if (energyDelta == 0 && !mFirstCollection) {
+            return null;
+        }
+
+        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+        mLastVoltageMv = voltageMv;
+        mLayout.setConsumedEnergy(mPowerStats.stats, 0, uJtoUc(energyDelta, averageVoltage));
+        long timestamp = mClock.elapsedRealtime();
+        mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
+        mLastUpdateTimestamp = timestamp;
+        mFirstCollection = false;
+        return mPowerStats;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java
new file mode 100644
index 0000000..f7216c9
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.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.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+
+public class FlashlightPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    public FlashlightPowerStatsProcessor(PowerProfile powerProfile,
+            PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT));
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states2 & BatteryStats.HistoryItem.STATE2_FLASHLIGHT_FLAG) != 0
+                ? STATE_ON
+                : STATE_OFF;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
new file mode 100644
index 0000000..168a874
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.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.server.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+
+public class GnssPowerStatsCollector extends EnergyConsumerPowerStatsCollector {
+
+    GnssPowerStatsCollector(Injector injector) {
+        super(injector, BatteryConsumer.POWER_COMPONENT_GNSS,
+                BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_GNSS),
+                EnergyConsumerType.GNSS, /* energy consumer name */ null,
+                new GnssPowerStatsLayout());
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java
new file mode 100644
index 0000000..9a1317d
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java
@@ -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.server.power.stats;
+
+import android.location.GnssSignalQuality;
+import android.os.PersistableBundle;
+
+class GnssPowerStatsLayout extends BinaryStatePowerStatsLayout {
+    private static final String EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION = "dt-sig";
+    private static final String EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION = "ut-sig";
+
+    private int mDeviceSignalLevelTimePosition;
+    private int mUidSignalLevelTimePosition;
+
+    GnssPowerStatsLayout() {
+        mDeviceSignalLevelTimePosition = addDeviceSection(
+                GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
+        mUidSignalLevelTimePosition = addUidSection(
+                GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
+    }
+
+    @Override
+    public void fromExtras(PersistableBundle extras) {
+        super.fromExtras(extras);
+        mDeviceSignalLevelTimePosition = extras.getInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION);
+        mUidSignalLevelTimePosition = extras.getInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION);
+    }
+
+    @Override
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+        extras.putInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION, mDeviceSignalLevelTimePosition);
+        extras.putInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION, mUidSignalLevelTimePosition);
+    }
+
+    public void setDeviceSignalLevelTime(long[] stats, int signalLevel, long durationMillis) {
+        stats[mDeviceSignalLevelTimePosition + signalLevel] = durationMillis;
+    }
+
+    public long getDeviceSignalLevelTime(long[] stats, int signalLevel) {
+        return stats[mDeviceSignalLevelTimePosition + signalLevel];
+    }
+
+    public void setUidSignalLevelTime(long[] stats, int signalLevel, long durationMillis) {
+        stats[mUidSignalLevelTimePosition + signalLevel] = durationMillis;
+    }
+
+    public long getUidSignalLevelTime(long[] stats, int signalLevel) {
+        return stats[mUidSignalLevelTimePosition + signalLevel];
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java
new file mode 100644
index 0000000..572bde9
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats;
+
+import android.location.GnssSignalQuality;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Process;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import java.util.Arrays;
+
+public class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    private int mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
+    private long mGnssSignalLevelTimestamp;
+    private final long[] mGnssSignalDurations =
+            new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
+    private static final GnssPowerStatsLayout sStatsLayout = new GnssPowerStatsLayout();
+    private final UsageBasedPowerEstimator[] mSignalLevelEstimators =
+            new UsageBasedPowerEstimator[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
+    private final boolean mUseSignalLevelEstimators;
+    private long[] mTmpDeviceStatsArray;
+
+    public GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON),
+                sStatsLayout);
+
+        boolean useSignalLevelEstimators = false;
+        for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+            double power = powerProfile.getAveragePower(
+                    PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, level);
+            if (power != 0) {
+                useSignalLevelEstimators = true;
+            }
+            mSignalLevelEstimators[level] = new UsageBasedPowerEstimator(power);
+        }
+        mUseSignalLevelEstimators = useSignalLevelEstimators;
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        if ((item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) == 0) {
+            mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
+            return STATE_OFF;
+        }
+
+        noteGnssSignalLevel(item);
+        return STATE_ON;
+    }
+
+    private void noteGnssSignalLevel(BatteryStats.HistoryItem item) {
+        int signalLevel = (item.states2 & BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+                >> BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
+        if (signalLevel >= GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) {
+            signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
+        }
+        if (signalLevel == mGnssSignalLevel) {
+            return;
+        }
+
+        if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) {
+            mGnssSignalDurations[mGnssSignalLevel] += item.time - mGnssSignalLevelTimestamp;
+        }
+        mGnssSignalLevel = signalLevel;
+        mGnssSignalLevelTimestamp = item.time;
+    }
+
+    @Override
+    protected void recordUsageDuration(PowerStats powerStats, int uid, long time) {
+        super.recordUsageDuration(powerStats, uid, time);
+
+        if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) {
+            mGnssSignalDurations[mGnssSignalLevel] += time - mGnssSignalLevelTimestamp;
+        } else if (mUseSignalLevelEstimators) {
+            // Default GNSS signal quality to GOOD for the purposes of power attribution
+            mGnssSignalDurations[GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD] +=
+                    time - mGnssSignalLevelTimestamp;
+        }
+
+        for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+            long duration = mGnssSignalDurations[level];
+            sStatsLayout.setDeviceSignalLevelTime(powerStats.stats, level, duration);
+            if (uid != Process.INVALID_UID) {
+                long[] uidStats = powerStats.uidStats.get(uid);
+                if (uidStats == null) {
+                    uidStats = new long[powerStats.descriptor.uidStatsArrayLength];
+                    powerStats.uidStats.put(uid, uidStats);
+                    sStatsLayout.setUidSignalLevelTime(uidStats, level, duration);
+                } else {
+                    sStatsLayout.setUidSignalLevelTime(uidStats, level,
+                            sStatsLayout.getUidSignalLevelTime(uidStats, level) + duration);
+                }
+            }
+        }
+
+        mGnssSignalLevelTimestamp = time;
+        Arrays.fill(mGnssSignalDurations, 0);
+    }
+
+    protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            PowerEstimationPlan plan, boolean energyConsumerSupported) {
+        if (!mUseSignalLevelEstimators || energyConsumerSupported) {
+            super.computeDevicePowerEstimates(stats, plan, energyConsumerSupported);
+            return;
+        }
+
+        if (mTmpDeviceStatsArray == null) {
+            mTmpDeviceStatsArray = new long[stats.getPowerStatsDescriptor().statsArrayLength];
+        }
+
+        for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i);
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+                continue;
+            }
+
+            double power = 0;
+            for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+                long duration = sStatsLayout.getDeviceSignalLevelTime(mTmpDeviceStatsArray, level);
+                power += mSignalLevelEstimators[level].calculatePower(duration);
+            }
+            sStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index b82c021..d442c61 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -231,10 +231,14 @@
     }
 
     interface ConsumedEnergyRetriever {
-        int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType);
+        int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType, String name);
 
         @Nullable
         long[] getConsumedEnergyUws(int[] energyConsumerIds);
+
+        default int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType) {
+            return getEnergyConsumerIds(energyConsumerType, null);
+        }
     }
 
     static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever {
@@ -245,7 +249,7 @@
         }
 
         @Override
-        public int[] getEnergyConsumerIds(int energyConsumerType) {
+        public int[] getEnergyConsumerIds(int energyConsumerType, String name) {
             if (mPowerStatsInternal == null) {
                 return new int[0];
             }
@@ -257,7 +261,8 @@
 
             List<EnergyConsumer> energyConsumers = new ArrayList<>();
             for (EnergyConsumer energyConsumer : energyConsumerInfo) {
-                if (energyConsumer.type == energyConsumerType) {
+                if (energyConsumer.type == energyConsumerType
+                        && (name == null || name.equals(energyConsumer.name))) {
                     energyConsumers.add(energyConsumer);
                 }
             }
diff --git a/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java
new file mode 100644
index 0000000..48dac8a
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.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.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+
+public class VideoPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    public VideoPowerStatsProcessor(PowerProfile powerProfile,
+            PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_VIDEO, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_VIDEO));
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states2 & BatteryStats.HistoryItem.STATE2_VIDEO_ON_FLAG) != 0
+                ? STATE_ON
+                : STATE_OFF;
+    }
+}
diff --git a/services/core/java/com/android/server/powerstats/Android.bp b/services/core/java/com/android/server/powerstats/Android.bp
new file mode 100644
index 0000000..7f3b091
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/Android.bp
@@ -0,0 +1,11 @@
+aconfig_declarations {
+    name: "powerstats_flags",
+    package: "com.android.server.powerstats",
+    container: "system",
+    srcs: ["*.aconfig"],
+}
+
+java_aconfig_library {
+    name: "powerstats_flags_lib",
+    aconfig_declarations: "powerstats_flags",
+}
diff --git a/services/core/java/com/android/server/powerstats/TimerTrigger.java b/services/core/java/com/android/server/powerstats/TimerTrigger.java
index f8a4135..817a40d 100644
--- a/services/core/java/com/android/server/powerstats/TimerTrigger.java
+++ b/services/core/java/com/android/server/powerstats/TimerTrigger.java
@@ -16,8 +16,10 @@
 
 package com.android.server.powerstats;
 
+import android.app.AlarmManager;
 import android.content.Context;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.util.Slog;
 
 /**
@@ -33,37 +35,53 @@
     private static final long LOG_PERIOD_MS_HIGH_FREQUENCY = 2 * 60 * 1000; // 2 minutes
 
     private final Handler mHandler;
+    private final AlarmManager mAlarmManager;
 
-    private Runnable mLogDataLowFrequency = new Runnable() {
+    class PeriodicTimer implements Runnable, AlarmManager.OnAlarmListener {
+        private final String mName;
+        private final long mPeriodMs;
+        private final int mMsgType;
+
+        PeriodicTimer(String name, long periodMs, int msgType) {
+            mName = name;
+            mPeriodMs = periodMs;
+            mMsgType = msgType;
+        }
+
+        @Override
+        public void onAlarm() {
+            run();
+        }
+
         @Override
         public void run() {
-            // Do not wake the device for these messages.  Opportunistically log rail data every
-            // LOG_PERIOD_MS_LOW_FREQUENCY.
-            mHandler.postDelayed(mLogDataLowFrequency, LOG_PERIOD_MS_LOW_FREQUENCY);
-            if (DEBUG) Slog.d(TAG, "Received delayed message.  Log rail data low frequency");
-            logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+            if (Flags.alarmBasedPowerstatsLogging()) {
+                final long nextAlarmMs = SystemClock.elapsedRealtime() + mPeriodMs;
+                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, nextAlarmMs,
+                        AlarmManager.WINDOW_EXACT, 0, mName, this, mHandler, null);
+            } else {
+                mHandler.postDelayed(this, mPeriodMs);
+            }
+            if (DEBUG) Slog.d(TAG, "Received delayed message (" + mName + ").  Logging rail data");
+            logPowerStatsData(mMsgType);
         }
-    };
-
-    private Runnable mLogDataHighFrequency = new Runnable() {
-        @Override
-        public void run() {
-            // Do not wake the device for these messages.  Opportunistically log rail data every
-            // LOG_PERIOD_MS_HIGH_FREQUENCY.
-            mHandler.postDelayed(mLogDataHighFrequency, LOG_PERIOD_MS_HIGH_FREQUENCY);
-            if (DEBUG) Slog.d(TAG, "Received delayed message.  Log rail data high frequency");
-            logPowerStatsData(PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
-        }
-    };
+    }
 
     public TimerTrigger(Context context, PowerStatsLogger powerStatsLogger,
             boolean triggerEnabled) {
         super(context, powerStatsLogger);
         mHandler = mContext.getMainThreadHandler();
+        mAlarmManager = mContext.getSystemService(AlarmManager.class);
 
         if (triggerEnabled) {
-            mLogDataLowFrequency.run();
-            mLogDataHighFrequency.run();
+            final PeriodicTimer logDataLowFrequency = new PeriodicTimer("PowerStatsLowFreqLog",
+                    LOG_PERIOD_MS_LOW_FREQUENCY,
+                    PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_LOW_FREQUENCY);
+            final PeriodicTimer logDataHighFrequency = new PeriodicTimer("PowerStatsHighFreqLog",
+                    LOG_PERIOD_MS_HIGH_FREQUENCY,
+                    PowerStatsLogger.MSG_LOG_TO_DATA_STORAGE_HIGH_FREQUENCY);
+            logDataLowFrequency.run();
+            logDataHighFrequency.run();
         }
     }
 }
diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig
new file mode 100644
index 0000000..0a4a751
--- /dev/null
+++ b/services/core/java/com/android/server/powerstats/flags.aconfig
@@ -0,0 +1,13 @@
+
+package: "com.android.server.powerstats"
+container: "system"
+
+flag {
+    name: "alarm_based_powerstats_logging"
+    namespace: "backstage_power"
+    description: "Utilize new OomAdjuster implementation"
+    bug: "294598168"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 4264e91..85c8900 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1296,7 +1296,7 @@
 
         synchronized (mIcons) {
             StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
-                    iconLevel, 0, contentDescription);
+                    iconLevel, 0, contentDescription, StatusBarIcon.Type.SystemIcon);
             //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
             mIcons.put(slot, icon);
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 04db3e8..3138a9e 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1870,6 +1870,11 @@
 
         @Override
         public boolean isInSignificantPlace() {
+            if (android.security.Flags.significantPlaces()) {
+                mSignificantPlaceServiceWatcher.runOnBinder(
+                        binder -> ISignificantPlaceProvider.Stub.asInterface(binder)
+                                .onSignificantPlaceCheck());
+            }
             return mIsInSignificantPlace;
         }
 
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
old mode 100755
new mode 100644
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 4af8c61..a581b08 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -218,10 +218,6 @@
                 mService.mMetricsHelper.registerPuller();
             }
         }
-
-        public UriGrantsManagerService getService() {
-            return mService;
-        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/utils/AnrTimer.java b/services/core/java/com/android/server/utils/AnrTimer.java
index c605a47..153bb91 100644
--- a/services/core/java/com/android/server/utils/AnrTimer.java
+++ b/services/core/java/com/android/server/utils/AnrTimer.java
@@ -19,6 +19,7 @@
 import static android.text.TextUtils.formatSimple;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
@@ -26,6 +27,7 @@
 import android.text.TextUtils;
 import android.text.format.TimeMigrationUtils;
 import android.util.ArrayMap;
+import android.util.CloseGuard;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.LongSparseArray;
@@ -93,7 +95,7 @@
      * tracks give a sense of handler latency: the time between timer expiration and ANR
      * collection.
      */
-    private final static String TRACK = "AnrTimer";
+    private final static String TRACK = "AnrTimerTrack";
 
     /**
      * Enable debug messages.
@@ -128,12 +130,23 @@
     }
 
     /**
+     * Return true if freezing is enabled.  This has no effect if the service is not enabled.
+     */
+    private static boolean anrTimerFreezerEnabled() {
+        return Flags.anrTimerFreezer();
+    }
+
+    /**
      * This class allows test code to provide instance-specific overrides.
      */
     static class Injector {
         boolean anrTimerServiceEnabled() {
             return AnrTimer.anrTimerServiceEnabled();
         }
+
+        boolean anrTimerFreezerEnabled() {
+            return AnrTimer.anrTimerFreezerEnabled();
+        }
     }
 
     /** The default injector. */
@@ -150,6 +163,9 @@
         /** Grant timer extensions when the system is heavily loaded. */
         private boolean mExtend = false;
 
+        /** Freeze ANR'ed processes. */
+        boolean mFreeze = false;
+
         // This is only used for testing, so it is limited to package visibility.
         Args injector(@NonNull Injector injector) {
             mInjector = injector;
@@ -160,6 +176,58 @@
             mExtend = flag;
             return this;
         }
+
+        public Args freeze(boolean enable) {
+            mFreeze = enable;
+            return this;
+        }
+    }
+
+    /**
+     * A target process may be modified when its timer expires.  The modification (if any) will be
+     * undone if the expiration is discarded, but is persisted if the expiration is accepted.  If
+     * the expiration is accepted, then a TimerLock is returned to the client.  The client must
+     * close the TimerLock to complete the state machine.
+     */
+    private class TimerLock implements AutoCloseable {
+        // Detect failures to close.
+        private final CloseGuard mGuard = new CloseGuard();
+
+        // A lock to ensure closing is thread-safe.
+        private final Object mLock = new Object();
+
+        // Allow multiple calls to close().
+        private boolean mClosed = false;
+
+        // The native timer ID that must be closed.  This may be zero.
+        final int mTimerId;
+
+        TimerLock(int timerId) {
+            mTimerId = timerId;
+            mGuard.open("AnrTimer.release");
+        }
+
+        @Override
+        public void close() {
+            synchronized (mLock) {
+                if (!mClosed) {
+                    AnrTimer.this.release(this);
+                    mGuard.close();
+                    mClosed = true;
+                }
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                // Note that guard could be null if the constructor threw.
+                if (mGuard != null) mGuard.warnIfOpen();
+                close();
+            } finally {
+                super.finalize();
+            }
+        }
     }
 
     /**
@@ -334,6 +402,7 @@
         final String label =
                 formatSimple("%s(%d,%d,%d,%s,%d)", op, timerId, pid, uid, mLabel, milliseconds);
         Trace.instantForTrack(TRACE_TAG, TRACK, label);
+        if (DEBUG) Log.i(TAG, label);
     }
 
     /**
@@ -342,6 +411,16 @@
     private void trace(String op, int timerId) {
         final String label = formatSimple("%s(%d)", op, timerId);
         Trace.instantForTrack(TRACE_TAG, TRACK, label);
+        if (DEBUG) Log.i(TAG, label);
+    }
+
+    /**
+     * Generate a trace point with a pid and uid but no timer ID.
+     */
+    private static void trace(String op, int pid, int uid) {
+        final String label = formatSimple("%s(%d,%d)", op, pid, uid);
+        Trace.instantForTrack(TRACE_TAG, TRACK, label);
+        if (DEBUG) Log.i(TAG, label);
     }
 
     /**
@@ -353,10 +432,13 @@
 
         abstract boolean cancel(@NonNull V arg);
 
-        abstract boolean accept(@NonNull V arg);
+        @Nullable
+        abstract TimerLock accept(@NonNull V arg);
 
         abstract boolean discard(@NonNull V arg);
 
+        abstract void release(@NonNull TimerLock timer);
+
         abstract boolean enabled();
 
         abstract void dump(IndentingPrintWriter pw, boolean verbose);
@@ -385,8 +467,9 @@
 
         /** accept() is a no-op when the feature is disabled. */
         @Override
-        boolean accept(@NonNull V arg) {
-            return true;
+        @Nullable
+        TimerLock accept(@NonNull V arg) {
+            return null;
         }
 
         /** discard() is a no-op when the feature is disabled. */
@@ -395,6 +478,11 @@
             return true;
         }
 
+        /** release() is a no-op when the feature is disabled. */
+        @Override
+        void release(@NonNull TimerLock timer) {
+        }
+
         /** The feature is not enabled. */
         @Override
         boolean enabled() {
@@ -441,9 +529,11 @@
         @GuardedBy("mLock")
         private int mTotalRestarted = 0;
 
-        /** Fetch the native tag (an integer) for the given label. */
+        /** Create the native AnrTimerService that will host all timers from this instance. */
         FeatureEnabled() {
-            mNative = nativeAnrTimerCreate(mLabel, mArgs.mExtend);
+            mNative = nativeAnrTimerCreate(mLabel,
+                    mArgs.mExtend,
+                    mArgs.mFreeze && mArgs.mInjector.anrTimerFreezerEnabled());
             if (mNative == 0) throw new IllegalArgumentException("unable to create native timer");
             synchronized (sAnrTimerList) {
                 sAnrTimerList.put(mNative, new WeakReference(AnrTimer.this));
@@ -494,19 +584,26 @@
 
         /**
          * Accept a timer in the framework-level handler.  The timeout has been accepted and the
-         * timeout handler is executing.
+         * client's timeout handler is executing.  If the function returns a non-null TimerLock then
+         * the associated process may have been paused (or otherwise modified in preparation for
+         * debugging). The TimerLock must be closed to allow the process to continue, or to be
+         * dumped in an AnrReport.
          */
         @Override
-        boolean accept(@NonNull V arg) {
+        @Nullable
+        TimerLock accept(@NonNull V arg) {
             synchronized (mLock) {
                 Integer timer = removeLocked(arg);
                 if (timer == null) {
                     notFoundLocked("accept", arg);
-                    return false;
+                    return null;
                 }
-                nativeAnrTimerAccept(mNative, timer);
+                boolean accepted = nativeAnrTimerAccept(mNative, timer);
                 trace("accept", timer);
-                return true;
+                // If "accepted" is true then the native layer has pending operations against this
+                // timer.  Wrap the timer ID in a TimerLock and return it to the caller.  If
+                // "accepted" is false then the native later does not have any pending operations.
+                return accepted ? new TimerLock(timer) : null;
             }
         }
 
@@ -529,6 +626,21 @@
             }
         }
 
+        /**
+         * Unfreeze an app that was frozen because its timer had expired.  This method catches
+         * errors that might be thrown by the unfreeze method.  This method does nothing if
+         * freezing is not enabled or if the AnrTimer never froze the timer.  Note that the native
+         * release method returns false only if the timer's process was frozen, is still frozen,
+         * and could not be unfrozen.
+         */
+        @Override
+        void release(@NonNull TimerLock t) {
+            if (t.mTimerId == 0) return;
+            if (!nativeAnrTimerRelease(mNative, t.mTimerId)) {
+                Log.e(TAG, "failed to release id=" + t.mTimerId, new Exception(TAG));
+            }
+        }
+
         /** The feature is enabled. */
         @Override
         boolean enabled() {
@@ -616,16 +728,20 @@
     /**
      * Accept the expired timer associated with arg.  This indicates that the caller considers the
      * timer expiration to be a true ANR.  (See {@link #discard} for an alternate response.)  The
-     * function returns true if an expired timer was found and false if a running timer was found or
-     * if no timer was found.  After this call, the timer does not exist.  It is an error to accept
-     * a running timer, however, the running timer will be canceled.
+     * function returns a {@link TimerLock} if an expired timer was found and null otherwise.
+     * After this call, the timer does not exist.  It is an error to accept a running timer,
+     * however, the running timer will be canceled.
      *
-     * Note: the return value is always true if the feature is not enabled.
+     * If a non-null TimerLock is returned, the TimerLock must be closed before the target process
+     * is dumped (for an ANR report) or continued.
+     *
+     * Note: the return value is always null if the feature is not enabled.
      *
      * @param arg The key by which the timer is known.  This is never examined or modified.
-     * @return True if an expired timer was accepted.
+     * @return A TimerLock if an expired timer was accepted.
      */
-    public boolean accept(@NonNull V arg) {
+    @Nullable
+    public TimerLock accept(@NonNull V arg) {
         return mFeature.accept(arg);
     }
 
@@ -647,6 +763,13 @@
     }
 
     /**
+     * Release an expired timer.
+     */
+    private void release(@NonNull TimerLock t) {
+        mFeature.release(t);
+    }
+
+    /**
      * The notifier that a timer has fired.  The timerId and original pid/uid are supplied.  The
      * elapsed time is the actual time since the timer was scheduled, which may be different from
      * the original timeout if the timer was extended or if other delays occurred. This method
@@ -826,7 +949,7 @@
      * Unlike the other methods, this is an instance method: the "this" parameter is passed into
      * the native layer.
      */
-    private native long nativeAnrTimerCreate(String name, boolean extend);
+    private native long nativeAnrTimerCreate(String name, boolean extend, boolean freeze);
 
     /** Release the native resources.  No further operations are premitted. */
     private static native int nativeAnrTimerClose(long service);
@@ -840,12 +963,24 @@
      */
     private static native boolean nativeAnrTimerCancel(long service, int timerId);
 
-    /** Accept an expired timer by ID.  Return true if the timer was found. */
+    /**
+     * Accept an expired timer by ID.  Return true if the timer must be released.  Return false if
+     * the native layer is completely finished with this timer.
+     */
     private static native boolean nativeAnrTimerAccept(long service, int timerId);
 
     /** Discard an expired timer by ID.  Return true if the timer was found.  */
     private static native boolean nativeAnrTimerDiscard(long service, int timerId);
 
+    /**
+     * Release (unfreeze) the process associated with the timer, if the process was previously
+     * frozen by the service.  The function returns false if three conditions are true: the timer
+     * does exist, the timer's process was frozen, and the timer's process could not be unfrozen.
+     * Otherwise, the function returns true.  In other words, a return value of value means there
+     * is a process that is unexpectedly stuck in the frozen state.
+     */
+    private static native boolean nativeAnrTimerRelease(long service, int timerId);
+
     /** 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 6f37837..00ebb66 100644
--- a/services/core/java/com/android/server/utils/flags.aconfig
+++ b/services/core/java/com/android/server/utils/flags.aconfig
@@ -8,3 +8,12 @@
      description: "Feature flag for the ANR timer service"
      bug: "282428924"
 }
+
+flag {
+     name: "anr_timer_freezer"
+     namespace: "system_performance"
+     is_fixed_read_only: true
+     description: "Enable freezing of a process when an ANR is triggered"
+     bug: "325594551"
+}
+
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 6710d02..988e8fe 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -56,10 +56,17 @@
     private volatile boolean mIsUnderExternalControl;
     private volatile float mCurrentAmplitude;
 
-    /** Listener for vibration completion callbacks from native. */
+    /**
+     * Listener for vibration completion callbacks from native.
+     *
+     * <p>Only the latest active native call to {@link VibratorController#on} will ever trigger this
+     * completion callback, to avoid race conditions during a vibration playback. If a new call to
+     * {@link #on} or {@link #off} happens before a previous callback was triggered then the
+     * previous callback will be disabled, even if the new command fails.
+     */
     public interface OnVibrationCompleteListener {
 
-        /** Callback triggered when vibration is complete. */
+        /** Callback triggered when an active vibration command is complete. */
         void onComplete(int vibratorId, long vibrationId);
     }
 
@@ -235,7 +242,7 @@
     }
 
     /**
-     * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} or completion
+     * Turn on the vibrator for {@code milliseconds} time, using {@code vibrationId} for completion
      * callback to {@link OnVibrationCompleteListener}.
      *
      * <p>This will affect the state of {@link #isVibrating()}.
@@ -255,7 +262,7 @@
     }
 
     /**
-     * Plays predefined vibration effect, using {@code vibrationId} or completion callback to
+     * Plays predefined vibration effect, using {@code vibrationId} for completion callback to
      * {@link OnVibrationCompleteListener}.
      *
      * <p>This will affect the state of {@link #isVibrating()}.
@@ -276,8 +283,8 @@
     }
 
     /**
-     * Plays a composition of vibration primitives, using {@code vibrationId} or completion callback
-     * to {@link OnVibrationCompleteListener}.
+     * Plays a composition of vibration primitives, using {@code vibrationId} for completion
+     * callback to {@link OnVibrationCompleteListener}.
      *
      * <p>This will affect the state of {@link #isVibrating()}.
      *
@@ -299,7 +306,7 @@
     }
 
     /**
-     * Plays a composition of pwle primitives, using {@code vibrationId} or completion callback
+     * Plays a composition of pwle primitives, using {@code vibrationId} for completion callback
      * to {@link OnVibrationCompleteListener}.
      *
      * <p>This will affect the state of {@link #isVibrating()}.
@@ -321,7 +328,11 @@
         }
     }
 
-    /** Turns off the vibrator. This will affect the state of {@link #isVibrating()}. */
+    /**
+     * Turns off the vibrator and disables completion callback to any pending vibration.
+     *
+     * <p>This will affect the state of {@link #isVibrating()}.
+     */
     public void off() {
         synchronized (mLock) {
             mNativeWrapper.off();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 80f1125..f70a3ba 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -150,7 +150,7 @@
                 Rect landscapeCrop = getCrop(rotatedDisplaySize, bitmapSize, suggestedCrops, rtl);
                 landscapeCrop = noParallax(landscapeCrop, rotatedDisplaySize, bitmapSize, rtl);
                 // compute the crop on portrait at the center of the landscape crop
-                crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, ADD);
+                crop = getAdjustedCrop(landscapeCrop, bitmapSize, displaySize, false, rtl, ADD);
 
                 // add some parallax (until the border of the landscape crop without parallax)
                 if (rtl) {
@@ -160,7 +160,7 @@
                 }
             }
 
-            return getAdjustedCrop(crop, bitmapSize, displaySize, true, ADD);
+            return getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD);
         }
 
         // If any suggested crop is invalid, fallback to case 1
@@ -176,7 +176,7 @@
         // Case 2: if the orientation exists in the suggested crops, adjust the suggested crop
         Rect suggestedCrop = suggestedCrops.get(orientation);
         if (suggestedCrop != null) {
-            return getAdjustedCrop(suggestedCrop, bitmapSize, displaySize, true, ADD);
+            return getAdjustedCrop(suggestedCrop, bitmapSize, displaySize, true, rtl, ADD);
         }
 
         // Case 3: if we have the 90° rotated orientation in the suggested crops, reuse it and
@@ -188,7 +188,7 @@
         if (suggestedCrop != null) {
             // only keep the visible part (without parallax)
             Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
-            return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, BALANCE);
+            return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, BALANCE);
         }
 
         // Case 4: if the device is a foldable, if we're looking for a folded orientation and have
@@ -200,13 +200,13 @@
             // compute the visible part (without parallax) of the unfolded screen
             Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
             // compute the folded crop, at the center of the crop of the unfolded screen
-            Rect res = getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, REMOVE);
+            Rect res = getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, REMOVE);
             // if we removed some width, add it back to add a parallax effect
             if (res.width() < adjustedCrop.width()) {
                 if (rtl) res.left = Math.min(res.left, adjustedCrop.left);
                 else res.right = Math.max(res.right, adjustedCrop.right);
                 // use getAdjustedCrop(parallax=true) to make sure we don't exceed MAX_PARALLAX
-                res = getAdjustedCrop(res, bitmapSize, displaySize, true, ADD);
+                res = getAdjustedCrop(res, bitmapSize, displaySize, true, rtl, ADD);
             }
             return res;
         }
@@ -220,7 +220,7 @@
         if (suggestedCrop != null) {
             // only keep the visible part (without parallax)
             Rect adjustedCrop = noParallax(suggestedCrop, suggestedDisplaySize, bitmapSize, rtl);
-            return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, ADD);
+            return getAdjustedCrop(adjustedCrop, bitmapSize, displaySize, false, rtl, ADD);
         }
 
         // Case 6: for a foldable device, try to combine case 3 + case 4 or 5:
@@ -255,7 +255,7 @@
     @VisibleForTesting
     static Rect noParallax(Rect crop, Point displaySize, Point bitmapSize, boolean rtl) {
         if (displaySize == null) return crop;
-        Rect adjustedCrop = getAdjustedCrop(crop, bitmapSize, displaySize, true, ADD);
+        Rect adjustedCrop = getAdjustedCrop(crop, bitmapSize, displaySize, true, rtl, ADD);
         // only keep the visible part (without parallax)
         float suggestedDisplayRatio = 1f * displaySize.x / displaySize.y;
         int widthToRemove = (int) (adjustedCrop.width()
@@ -272,7 +272,7 @@
      * Adjust a given crop:
      * <ul>
      *     <li>If parallax = true, make sure we have a parallax of at most {@link #MAX_PARALLAX},
-     *     by removing content from both sides if necessary.
+     *     by removing content from the right (or left if RTL) if necessary.
      *     <li>If parallax = false, make sure we do not have additional width for parallax. If we
      *     have additional width for parallax, remove half of the additional width on both sides.
      *     <li>Make sure the crop fills the screen, i.e. that the width/height ratio of the crop
@@ -282,7 +282,7 @@
      */
     @VisibleForTesting
     static Rect getAdjustedCrop(Rect crop, Point bitmapSize, Point screenSize,
-            boolean parallax, int mode) {
+            boolean parallax, boolean rtl, int mode) {
         Rect adjustedCrop = new Rect(crop);
         float cropRatio = ((float) crop.width()) / crop.height();
         float screenRatio = ((float) screenSize.x) / screenSize.y;
@@ -297,7 +297,8 @@
                 Rect rotatedCrop = new Rect(newLeft, newTop, newRight, newBottom);
                 Point rotatedBitmap = new Point(bitmapSize.y, bitmapSize.x);
                 Point rotatedScreen = new Point(screenSize.y, screenSize.x);
-                Rect rect = getAdjustedCrop(rotatedCrop, rotatedBitmap, rotatedScreen, false, mode);
+                Rect rect = getAdjustedCrop(
+                        rotatedCrop, rotatedBitmap, rotatedScreen, false, rtl, mode);
                 int resultLeft = rect.top;
                 int resultRight = resultLeft + rect.height();
                 int resultTop = rotatedBitmap.x - rect.right;
@@ -308,8 +309,11 @@
             if (additionalWidthForParallax > MAX_PARALLAX) {
                 int widthToRemove = (int) Math.ceil(
                         (additionalWidthForParallax - MAX_PARALLAX) * screenRatio * crop.height());
-                adjustedCrop.left += widthToRemove / 2;
-                adjustedCrop.right -= widthToRemove / 2 + widthToRemove % 2;
+                if (rtl) {
+                    adjustedCrop.left += widthToRemove;
+                } else {
+                    adjustedCrop.right -= widthToRemove;
+                }
             }
         } else {
             // Note: the third case when MODE == BALANCE, -W + sqrt(W * H * R), is the width to add
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index abc6bf6..b89120b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.wallpaper;
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
 import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.WallpaperManager.COMMAND_REAPPLY;
@@ -100,7 +101,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
@@ -2207,10 +2207,7 @@
             IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId,
             boolean getCropped) {
         final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL);
-        if (!hasPrivilege) {
-            mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
-                    Binder.getCallingPid(), Binder.getCallingUid(), callingPkg, callingFeatureId);
-        }
+        if (!hasPrivilege) checkPermission(MANAGE_EXTERNAL_STORAGE);
 
         wallpaperUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), wallpaperUserId, false, true, "getWallpaper", null);
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index 8d14c1d..ac419a5 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -33,7 +33,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
@@ -50,11 +49,9 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.infra.AbstractMasterSystemService;
 import com.android.server.infra.FrameworkResourcesServiceNameResolver;
-import com.android.server.pm.KnownPackages;
 import com.android.server.utils.quota.MultiRateLimiter;
 
 import java.io.FileDescriptor;
@@ -196,16 +193,6 @@
         return MAX_TEMPORARY_SERVICE_DURATION_MS;
     }
 
-    /** Returns {@code true} if the detection service is configured on this device. */
-    public static boolean isDetectionServiceConfigured() {
-        final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-        final String[] packageNames = pmi.getKnownPackageNames(
-                KnownPackages.PACKAGE_WEARABLE_SENSING, UserHandle.USER_SYSTEM);
-        boolean isServiceConfigured = (packageNames.length != 0);
-        Slog.i(TAG, "Wearable sensing service configured: " + isServiceConfigured);
-        return isServiceConfigured;
-    }
-
     /**
      * Returns the AmbientContextManagerPerUserService component for this user.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index c9395da..3e177c9 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1299,9 +1299,11 @@
             // The restore windowing mode must be set after the windowing mode is set since
             // Task#setWindowingMode resets the restore windowing mode to WINDOWING_MODE_INVALID.
             requester.mMultiWindowRestoreWindowingMode = restoreWindowingMode;
+            requester.mMultiWindowRestoreParent =
+                    requester.getParent().mRemoteToken.toWindowContainerToken();
         } else {
             targetWindowingMode = requester.mMultiWindowRestoreWindowingMode;
-            requester.setWindowingMode(targetWindowingMode);
+            requester.restoreWindowingMode();
         }
         if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) {
             requester.setBounds(null);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2d2a88a86..d9dc7ba 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -37,6 +37,7 @@
 import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
 import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
 import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
 import static android.app.CameraCompatTaskInfo.cameraCompatControlStateToString;
 import static android.app.WaitResult.INVALID_DELAY;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -46,6 +47,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_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;
@@ -156,6 +158,7 @@
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityRecord.State.DESTROYED;
@@ -661,7 +664,8 @@
      */
     private CompatDisplayInsets mCompatDisplayInsets;
 
-    private final TaskFragment.ConfigOverrideHint mResolveConfigHint;
+    @VisibleForTesting
+    final TaskFragment.ConfigOverrideHint mResolveConfigHint;
 
     private final boolean mOptOutEdgeToEdge;
 
@@ -803,6 +807,11 @@
     final LetterboxUiController mLetterboxUiController;
 
     /**
+     * The policy for transparent activities
+     */
+    final TransparentPolicy mTransparentPolicy;
+
+    /**
      * The scale to fit at least one side of the activity to its parent. If the activity uses
      * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
      */
@@ -848,7 +857,6 @@
     @CameraCompatControlState
     private int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;
 
-
     // The callback that allows to ask the calling View to apply the treatment for stretched
     // issues affecting camera viewfinders when the user clicks on the camera compat control.
     @Nullable
@@ -1698,7 +1706,7 @@
             if (isState(RESUMED)) {
                 newParent.setResumedActivity(this, "onParentChanged");
             }
-            mLetterboxUiController.updateInheritedLetterbox();
+            mTransparentPolicy.start();
         }
 
         if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -2136,6 +2144,7 @@
         // Don't move below setOrientation(info.screenOrientation) since it triggers
         // getOverrideOrientation that requires having mLetterboxUiController
         // initialised.
+        mTransparentPolicy = new TransparentPolicy(this, mWmService.mLetterboxConfiguration);
         mLetterboxUiController = new LetterboxUiController(mWmService, this);
         mCameraCompatControlEnabled = mWmService.mContext.getResources()
                 .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);
@@ -2975,7 +2984,7 @@
 
     void removeStartingWindowAnimation(boolean prepareAnimation) {
         mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
-        if (task != null) {
+        if (mStartingData != null && task != null) {
             task.mSharedStartingData = null;
         }
         if (mStartingWindow == null) {
@@ -4026,6 +4035,10 @@
         if (next == null) {
             mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, mDisplayContent,
                     true /* deferResume */);
+            if (mDisplayContent.topRunningActivity() == null) {
+                // The transition is ready on a display with no running activities.
+                mTransitionController.setReady(mDisplayContent);
+            }
         }
         if (activityRemoved) {
             mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -6021,14 +6034,7 @@
                             true /* activityChange */, true /* updateOomAdj */,
                             true /* addPendingTopUid */);
                 }
-                final ContentCaptureManagerInternal contentCaptureService =
-                        LocalServices.getService(ContentCaptureManagerInternal.class);
-                if (contentCaptureService != null) {
-                    contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent,
-                            ActivityEvent.TYPE_ACTIVITY_STARTED,
-                            new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID,
-                                    shareableActivityToken));
-                }
+                mAtmService.mH.post(this::notifyActivityStartedToContentCaptureService);
                 break;
             case PAUSED:
                 mAtmService.updateBatteryStats(this, false);
@@ -6060,6 +6066,22 @@
         }
     }
 
+    private void notifyActivityStartedToContentCaptureService() {
+        final ContentCaptureManagerInternal contentCaptureService =
+                LocalServices.getService(ContentCaptureManagerInternal.class);
+        if (contentCaptureService != null) {
+            // For ACTIVITY_STARTED content capture is directly invoked to avoid persisting
+            // to UsageStats.
+            contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent,
+                    ActivityEvent.TYPE_ACTIVITY_STARTED,
+                    new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID,
+                            shareableActivityToken));
+
+            contentCaptureService.sendActivityStartAssistData(mUserId,
+                                shareableActivityToken, intent);
+        }
+    }
+
     State getState() {
         return mState;
     }
@@ -8080,13 +8102,13 @@
     @Configuration.Orientation
     int getRequestedConfigurationOrientation(boolean forDisplay,
             @ActivityInfo.ScreenOrientation int requestedOrientation) {
-        if (mLetterboxUiController.hasInheritedOrientation()) {
+        if (mTransparentPolicy.hasInheritedOrientation()) {
             final RootDisplayArea root = getRootDisplayArea();
             if (forDisplay && root != null && root.isOrientationDifferentFromDisplay()) {
                 return reverseConfigurationOrientation(
-                        mLetterboxUiController.getInheritedOrientation());
+                        mTransparentPolicy.getInheritedOrientation());
             } else {
-                return mLetterboxUiController.getInheritedOrientation();
+                return mTransparentPolicy.getInheritedOrientation();
             }
         }
         if (task != null && requestedOrientation == SCREEN_ORIENTATION_BEHIND) {
@@ -8302,8 +8324,8 @@
 
     @Nullable
     CompatDisplayInsets getCompatDisplayInsets() {
-        if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
-            return mLetterboxUiController.getInheritedCompatDisplayInsets();
+        if (mTransparentPolicy.isRunning()) {
+            return mTransparentPolicy.getInheritedCompatDisplayInsets();
         }
         return mCompatDisplayInsets;
     }
@@ -8466,7 +8488,7 @@
         }
         mSizeCompatBounds = null;
         mCompatDisplayInsets = null;
-        mLetterboxUiController.clearInheritedCompatDisplayInsets();
+        mTransparentPolicy.clearInheritedCompatDisplayInsets();
     }
 
     @VisibleForTesting
@@ -8527,17 +8549,23 @@
         mIsEligibleForFixedOrientationLetterbox = false;
         mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
         mLetterboxBoundsForAspectRatio = null;
+        mResolveConfigHint.resolveTmpOverrides(mDisplayContent, newParentConfiguration,
+                isFixedRotationTransforming());
 
         // Can't use resolvedConfig.windowConfiguration.getWindowingMode() because it can be
         // different from windowing mode of the task (PiP) during transition from fullscreen to PiP
         // and back which can cause visible issues (see b/184078928).
         final int parentWindowingMode =
                 newParentConfiguration.windowConfiguration.getWindowingMode();
+        final boolean isInCameraCompatFreeform = parentWindowingMode == WINDOWING_MODE_FREEFORM
+                && mLetterboxUiController.getFreeformCameraCompatMode()
+                        != CAMERA_COMPAT_FREEFORM_NONE;
 
         // Bubble activities should always fill their parent and should not be letterboxed.
         final boolean isFixedOrientationLetterboxAllowed = !getLaunchedFromBubble()
                 && (parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
                         || parentWindowingMode == WINDOWING_MODE_FULLSCREEN
+                        || isInCameraCompatFreeform
                         // When starting to switch between PiP and fullscreen, the task is pinned
                         // and the activity is fullscreen. But only allow to apply letterbox if the
                         // activity is exiting PiP because an entered PiP should fill the task.
@@ -8641,10 +8669,15 @@
         }
 
         applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
+        mResolveConfigHint.resetTmpOverrides();
 
         logAppCompatState();
     }
 
+    @Nullable Rect getParentAppBoundsOverride() {
+        return Rect.copyOrNull(mResolveConfigHint.mTmpParentAppBoundsOverride);
+    }
+
     /**
      * If necessary, override configuration fields related to app bounds.
      * This will happen when the app is targeting SDK earlier than 35.
@@ -8668,8 +8701,14 @@
             rotation = mDisplayContent.getRotation();
         }
         if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig
-                || getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets()
-                || isFloating(parentWindowingMode) || rotation == ROTATION_UNDEFINED)) {
+                || getCompatDisplayInsets() != null
+                || (isFloating(parentWindowingMode)
+                        // Check the requested windowing mode of activity as well in case it is
+                        // switching between PiP and fullscreen.
+                        && (inOutConfig.windowConfiguration.getWindowingMode()
+                                == WINDOWING_MODE_UNDEFINED
+                                || isFloating(inOutConfig.windowConfiguration.getWindowingMode())))
+                || rotation == ROTATION_UNDEFINED)) {
             // If the insets configuration decoupled logic is not enabled for the app, or the app
             // already has a compat override, or the context doesn't contain enough info to
             // calculate the override, skip the override.
@@ -8691,7 +8730,7 @@
                 : mDisplayContent.mBaseDisplayWidth;
         final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
                 : mDisplayContent.mBaseDisplayHeight;
-        final  Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
+        final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
                 .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets;
         // This should be the only place override the configuration for ActivityRecord. Override
         // the value if not calculated yet.
@@ -8707,12 +8746,10 @@
         }
         density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
         if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
-            final int overrideScreenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
-            inOutConfig.screenWidthDp = overrideScreenWidthDp;
+            inOutConfig.screenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
         }
         if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-            final int overrideScreenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
-            inOutConfig.screenHeightDp = overrideScreenHeightDp;
+            inOutConfig.screenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
         }
         if (inOutConfig.smallestScreenWidthDp
                 == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
@@ -8784,8 +8821,8 @@
             return APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
         }
         // TODO(b/256564921): Investigate if we need new metrics for translucent activities
-        if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
-            return mLetterboxUiController.getInheritedAppCompatState();
+        if (mTransparentPolicy.isRunning()) {
+            return mTransparentPolicy.getInheritedAppCompatState();
         }
         if (mInSizeCompatModeForBounds) {
             return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
@@ -8823,7 +8860,7 @@
         }
         final Rect screenResolvedBounds =
                 mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds;
-        final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+        final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride;
         final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
         final float screenResolvedBoundsWidth = screenResolvedBounds.width();
         final float parentAppBoundsWidth = parentAppBounds.width();
@@ -8938,7 +8975,7 @@
         // We check if the current activity is transparent. In that case we need to
         // recomputeConfiguration of the first opaque activity beneath, to allow a
         // proper computation of the new bounds.
-        if (!mLetterboxUiController.applyOnOpaqueActivityBelow(
+        if (!mTransparentPolicy.applyOnOpaqueActivityBelow(
                 ActivityRecord::recomputeConfiguration)) {
             onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
         }
@@ -9232,7 +9269,7 @@
      */
     private void resolveAspectRatioRestriction(Configuration newParentConfiguration) {
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
-        final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
+        final Rect parentAppBounds = mResolveConfigHint.mTmpParentAppBoundsOverride;
         final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
         // Use tmp bounds to calculate aspect ratio so we can know whether the activity should use
@@ -9261,19 +9298,6 @@
             @NonNull CompatDisplayInsets compatDisplayInsets) {
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
-        final Insets insets;
-        if (mResolveConfigHint.mUseOverrideInsetsForConfig) {
-            // TODO(b/343197837): Add test to verify SCM behaviour with new bound configuration
-            // Insets are decoupled from configuration by default from V+, use legacy
-            // compatibility behaviour for apps targeting SDK earlier than 35
-            // (see applySizeOverrideIfNeeded).
-            insets = Insets.of(mDisplayContent.getDisplayPolicy()
-                    .getDecorInsetsInfo(mDisplayContent.mDisplayFrames.mRotation,
-                            mDisplayContent.mDisplayFrames.mWidth,
-                            mDisplayContent.mDisplayFrames.mHeight).mOverrideNonDecorInsets);
-        } else {
-            insets = Insets.NONE;
-        }
 
         // When an activity needs to be letterboxed because of fixed orientation, use fixed
         // orientation bounds (stored in resolved bounds) instead of parent bounds since the
@@ -9284,22 +9308,22 @@
         final Rect containerBounds = useResolvedBounds
                 ? new Rect(resolvedBounds)
                 : newParentConfiguration.windowConfiguration.getBounds();
-        final Rect parentAppBounds =
-                newParentConfiguration.windowConfiguration.getAppBounds();
-        parentAppBounds.inset(insets);
         final Rect containerAppBounds = useResolvedBounds
                 ? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
-                : parentAppBounds;
+                : mResolveConfigHint.mTmpParentAppBoundsOverride;
 
         final int requestedOrientation = getRequestedConfigurationOrientation();
         final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
+        final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForConfig
+                ? mResolveConfigHint.mTmpOverrideConfigOrientation
+                : newParentConfiguration.orientation;
         final int orientation = orientationRequested
                 ? requestedOrientation
                 // We should use the original orientation of the activity when possible to avoid
                 // forcing the activity in the opposite orientation.
                 : compatDisplayInsets.mOriginalRequestedOrientation != ORIENTATION_UNDEFINED
                         ? compatDisplayInsets.mOriginalRequestedOrientation
-                        : newParentConfiguration.orientation;
+                        : parentOrientation;
         int rotation = newParentConfiguration.windowConfiguration.getRotation();
         final boolean isFixedToUserRotation = mDisplayContent == null
                 || mDisplayContent.getDisplayRotation().isFixedToUserRotation();
@@ -9341,7 +9365,7 @@
         // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
         // the parent bounds appropriately.
         if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
-            resolvedConfig.orientation = newParentConfiguration.orientation;
+            resolvedConfig.orientation = parentOrientation;
         }
 
         // Below figure is an example that puts an activity which was launched in a larger container
@@ -9411,7 +9435,7 @@
 
     void updateSizeCompatScale(Rect resolvedAppBounds, Rect containerAppBounds) {
         // Only allow to scale down.
-        mSizeCompatScale = mLetterboxUiController.findOpaqueNotFinishingActivityBelow()
+        mSizeCompatScale = mTransparentPolicy.findOpaqueNotFinishingActivityBelow()
                 .map(activityRecord -> activityRecord.mSizeCompatScale)
                 .orElseGet(() -> {
                     final int contentW = resolvedAppBounds.width();
@@ -9424,7 +9448,7 @@
     }
 
     private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) {
-        if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
+        if (mTransparentPolicy.isRunning()) {
             // To avoid wrong app behaviour, we decided to disable SCM when a translucent activity
             // is letterboxed.
             return false;
@@ -9487,7 +9511,7 @@
     public Rect getBounds() {
         // TODO(b/268458693): Refactor configuration inheritance in case of translucent activities
         final Rect superBounds = super.getBounds();
-        return mLetterboxUiController.findOpaqueNotFinishingActivityBelow()
+        return mTransparentPolicy.findOpaqueNotFinishingActivityBelow()
                 .map(ActivityRecord::getBounds)
                 .orElseGet(() -> {
                     if (mSizeCompatBounds != null) {
@@ -9851,8 +9875,8 @@
      * Returns the min aspect ratio of this activity.
      */
     float getMinAspectRatio() {
-        if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
-            return mLetterboxUiController.getInheritedMinAspectRatio();
+        if (mTransparentPolicy.isRunning()) {
+            return mTransparentPolicy.getInheritedMinAspectRatio();
         }
         if (info.applicationInfo == null) {
             return info.getMinAspectRatio();
@@ -9902,8 +9926,8 @@
     }
 
     float getMaxAspectRatio() {
-        if (mLetterboxUiController.hasInheritedLetterboxBehavior()) {
-            return mLetterboxUiController.getInheritedMaxAspectRatio();
+        if (mTransparentPolicy.isRunning()) {
+            return mTransparentPolicy.getInheritedMaxAspectRatio();
         }
         return info.getMaxAspectRatio();
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a9192c4c..e6d8132 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -139,6 +139,7 @@
 import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
+import com.android.wm.shell.Flags;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -1365,18 +1366,6 @@
             request.outActivity[0] = mLastStartActivityRecord;
         }
 
-        // Reset the ActivityRecord#mCurrentLaunchCanTurnScreenOn state of activity started
-        // before this one if it is no longer the top-most focusable activity.
-        // Doing so in case the state is not yet consumed during rapid activity launch.
-        if (previousStart != null && !previousStart.finishing && previousStart.isAttached()
-                && previousStart.currentLaunchCanTurnScreenOn()) {
-            final ActivityRecord topFocusable = previousStart.getDisplayContent().getActivity(
-                    ar -> ar.isFocusable() && !ar.finishing);
-            if (previousStart != topFocusable) {
-                previousStart.setCurrentLaunchCanTurnScreenOn(false);
-            }
-        }
-
         return mLastStartActivityResult;
     }
 
@@ -1735,7 +1724,14 @@
         // Get top task at beginning because the order may be changed when reusing existing task.
         final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
         final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
-        final Task reusedTask = resolveReusableTask();
+        final boolean sourceActivityLaunchedFromBubble =
+                sourceRecord != null && sourceRecord.getLaunchedFromBubble();
+        // if the flag is enabled, allow reusing bubbled tasks only if the source activity is
+        // bubbled.
+        final boolean includeLaunchedFromBubble =
+                Flags.onlyReuseBubbledTaskWhenLaunchedFromBubble()
+                        ? sourceActivityLaunchedFromBubble : true;
+        final Task reusedTask = resolveReusableTask(includeLaunchedFromBubble);
 
         // If requested, freeze the task list
         if (mOptions != null && mOptions.freezeRecentTasksReordering()
@@ -2734,8 +2730,11 @@
     /**
      * Decide whether the new activity should be inserted into an existing task. Returns null
      * if not or an ActivityRecord with the task into which the new activity should be added.
+     *
+     * @param includeLaunchedFromBubble whether a task whose top activity was launched from a bubble
+     *                                  should be allowed to be reused for the new activity.
      */
-    private Task resolveReusableTask() {
+    private Task resolveReusableTask(boolean includeLaunchedFromBubble) {
         // If a target task is specified, try to reuse that one
         if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
             Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
@@ -2779,7 +2778,8 @@
             } else {
                 // Otherwise find the best task to put the activity in.
                 intentActivity =
-                        mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
+                        mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea,
+                                includeLaunchedFromBubble);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 3aa63af..cfd5300 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1439,6 +1439,8 @@
                 resultTo.removeResultsLocked(r, resultWho, requestCode);
             }
 
+            final int origCallingUid = Binder.getCallingUid();
+            final int origCallingPid = Binder.getCallingPid();
             final long origId = Binder.clearCallingIdentity();
             // TODO(b/64750076): Check if calling pid should really be -1.
             try {
@@ -1446,13 +1448,14 @@
                     options = new SafeActivityOptions(ActivityOptions.makeBasic());
                 }
 
-                // Fixes b/230492947
+                // Fixes b/230492947 b/337726734
                 // Prevents background activity launch through #startNextMatchingActivity
-                // An activity going into the background could still go back to the foreground
-                // if the intent used matches both:
-                // - the activity in the background
-                // - a second activity.
-                options.getOptions(r).setAvoidMoveToFront();
+                // launchedFromUid of the calling activity represents the app that launches it.
+                // It may have BAL privileges (i.e. the Launcher App). Using its identity to
+                // launch to launch next matching activity causes BAL.
+                // Change the realCallingUid to the calling activity's uid.
+                // In ActivityStarter, when caller is set, the callingUid and callingPid are
+                // ignored. So now both callingUid and realCallingUid is set to the caller app.
                 final int res = getActivityStartController()
                         .obtainStarter(intent, "startNextMatchingActivity")
                         .setCaller(r.app.getThread())
@@ -1465,8 +1468,8 @@
                         .setCallingUid(r.launchedFromUid)
                         .setCallingPackage(r.launchedFromPackage)
                         .setCallingFeatureId(r.launchedFromFeatureId)
-                        .setRealCallingPid(-1)
-                        .setRealCallingUid(r.launchedFromUid)
+                        .setRealCallingPid(origCallingPid)
+                        .setRealCallingUid(origCallingUid)
                         .setActivityOptions(options)
                         .setUserId(userId)
                         .execute();
@@ -1507,7 +1510,7 @@
         a.persistableMode = ActivityInfo.PERSIST_NEVER;
         a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
         a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
-        a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
+        a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
         a.configChanges = 0xffffffff;
 
         if (homePanelDream()) {
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index f91ef1d..1ce324f 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -200,6 +201,9 @@
             }
             infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback());
             infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback());
+            infoBuilder.setTouchableRegion(window.getFrame());
+            infoBuilder.setAppProgressAllowed((window.getAttrs().privateFlags
+                    & PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED) != 0);
             mNavigationMonitor.startMonitor(window, navigationObserver);
 
             ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, "
@@ -1365,8 +1369,6 @@
                                         ? task.getSurfaceControl()
                                         : mAdaptors[0].mTarget.getSurfaceControl());
                     }
-                    // remove starting surface.
-                    mStartingSurface = null;
                 }
             }
 
@@ -1383,7 +1385,10 @@
                         .removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
                                 !openTransitionMatch);
                 mRequestedStartingSurfaceId = INVALID_TASK_ID;
-                mStartingSurface = null;
+                if (mStartingSurface != null && mStartingSurface.isValid()) {
+                    mStartingSurface.release();
+                    mStartingSurface = null;
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 19d7a3c..a4fb959 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -427,19 +427,6 @@
             return name + "[debugOnly]";
         }
 
-        /** @return valid targetSdk or <code>-1</code> */
-        private int getTargetSdk(String packageName) {
-            if (packageName == null) {
-                return -1;
-            }
-            try {
-                PackageManager pm = mService.mContext.getPackageManager();
-                return pm.getTargetSdkVersion(packageName);
-            } catch (Exception e) {
-                return -1;
-            }
-        }
-
         private boolean hasRealCaller() {
             return mRealCallingUid != NO_PROCESS_UID;
         }
@@ -1730,7 +1717,9 @@
                 state.mResultForRealCaller == null ? BAL_BLOCK
                         : state.mResultForRealCaller.getRawCode(),
                 state.mBalAllowedByPiSender.allowsBackgroundActivityStarts(),
-                state.realCallerExplicitOptInOrOut()
+                state.realCallerExplicitOptInOrOut(),
+                getTargetSdk(state.mCallingPackage),
+                getTargetSdk(state.mRealCallingPackage)
         );
     }
 
@@ -1811,6 +1800,19 @@
                 + ", taskFragment=" + ar.getTaskFragment();
     }
 
+    /** @return valid targetSdk or <code>-1</code> */
+    private int getTargetSdk(String packageName) {
+        if (packageName == null) {
+            return -1;
+        }
+        try {
+            PackageManager pm = mService.mContext.getPackageManager();
+            return pm.getTargetSdkVersion(packageName);
+        } catch (Exception e) {
+            return -1;
+        }
+    }
+
     private class FinishedActivityEntry {
         int mUid;
         int mTaskId;
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
new file mode 100644
index 0000000..0c751cf
--- /dev/null
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -0,0 +1,196 @@
+/*
+ * 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.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.annotation.NonNull;
+import android.app.CameraCompatTaskInfo;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLogGroup;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.window.flags.Flags;
+
+/**
+ * Policy for camera compatibility freeform treatment.
+ *
+ * <p>The treatment is applied to a fixed-orientation camera activity in freeform windowing mode.
+ * The treatment letterboxes or pillarboxes the activity to the expected orientation and provides
+ * changes to the camera and display orientation signals to match those expected on a portrait
+ * device in that orientation (for example, on a standard phone).
+ */
+final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompatStateListener,
+        ActivityRefresher.Evaluator {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "CameraCompatFreeformPolicy" : TAG_WM;
+
+    @NonNull
+    private final DisplayContent mDisplayContent;
+    @NonNull
+    private final ActivityRefresher mActivityRefresher;
+    @NonNull
+    private final CameraStateMonitor mCameraStateMonitor;
+
+    private boolean mIsCameraCompatTreatmentPending = false;
+
+    CameraCompatFreeformPolicy(@NonNull DisplayContent displayContent,
+            @NonNull CameraStateMonitor cameraStateMonitor,
+            @NonNull ActivityRefresher activityRefresher) {
+        mDisplayContent = displayContent;
+        mCameraStateMonitor = cameraStateMonitor;
+        mActivityRefresher = activityRefresher;
+    }
+
+    void start() {
+        mCameraStateMonitor.addCameraStateListener(this);
+        mActivityRefresher.addEvaluator(this);
+    }
+
+    /** Releases camera callback listener. */
+    void dispose() {
+        mCameraStateMonitor.removeCameraStateListener(this);
+        mActivityRefresher.removeEvaluator(this);
+    }
+
+    // Refreshing only when configuration changes after rotation or camera split screen aspect ratio
+    // treatment is enabled.
+    @Override
+    public boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
+            @NonNull Configuration newConfig,
+            @NonNull Configuration lastReportedConfig) {
+        return isTreatmentEnabledForActivity(activity) && mIsCameraCompatTreatmentPending;
+    }
+
+    /**
+     * Whether activity is eligible for camera compatibility free-form treatment.
+     *
+     * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
+     * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
+     * provides changes to the camera and display orientation signals to match those expected on a
+     * portrait device in that orientation (for example, on a standard phone).
+     *
+     * <p>The treatment is enabled when the following conditions are met:
+     * <ul>
+     *     <li>Property gating the camera compatibility free-form treatment is enabled.
+     *     <li>Activity isn't opted out by the device manufacturer with override.
+     * </ul>
+     */
+    @VisibleForTesting
+    boolean shouldApplyFreeformTreatmentForCameraCompat(@NonNull ActivityRecord activity) {
+        return Flags.cameraCompatForFreeform() && !activity.info.isChangeEnabled(
+                ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
+    }
+
+    @Override
+    public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
+            @NonNull String cameraId) {
+        if (!isTreatmentEnabledForActivity(cameraActivity)) {
+            return false;
+        }
+        final int existingCameraCompatMode =
+                cameraActivity.mLetterboxUiController.getFreeformCameraCompatMode();
+        final int newCameraCompatMode = getCameraCompatMode(cameraActivity);
+        if (newCameraCompatMode != existingCameraCompatMode) {
+            mIsCameraCompatTreatmentPending = true;
+            cameraActivity.mLetterboxUiController.setFreeformCameraCompatMode(newCameraCompatMode);
+            forceUpdateActivityAndTask(cameraActivity);
+            return true;
+        } else {
+            mIsCameraCompatTreatmentPending = false;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onCameraClosed(@NonNull ActivityRecord cameraActivity,
+            @NonNull String cameraId) {
+        if (isActivityForCameraIdRefreshing(cameraId)) {
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_STATES,
+                    "Display id=%d is notified that Camera %s is closed but activity is"
+                            + " still refreshing. Rescheduling an update.",
+                    mDisplayContent.mDisplayId, cameraId);
+            return false;
+        }
+        cameraActivity.mLetterboxUiController.setFreeformCameraCompatMode(
+                CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE);
+        forceUpdateActivityAndTask(cameraActivity);
+        mIsCameraCompatTreatmentPending = false;
+        return true;
+    }
+
+    private void forceUpdateActivityAndTask(ActivityRecord cameraActivity) {
+        cameraActivity.recomputeConfiguration();
+        cameraActivity.updateReportedConfigurationAndSend();
+        Task cameraTask = cameraActivity.getTask();
+        if (cameraTask != null) {
+            cameraTask.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
+        }
+    }
+
+    private static int getCameraCompatMode(@NonNull ActivityRecord topActivity) {
+        return switch (topActivity.getRequestedConfigurationOrientation()) {
+            case ORIENTATION_PORTRAIT -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT;
+            case ORIENTATION_LANDSCAPE -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE;
+            default -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
+        };
+    }
+
+    /**
+     * Whether camera compat treatment is applicable for the given activity, ignoring its windowing
+     * mode.
+     *
+     * <p>Conditions that need to be met:
+     * <ul>
+     *     <li>Treatment is enabled.
+     *     <li>Camera is active for the package.
+     *     <li>The app has a fixed orientation.
+     *     <li>The app is in freeform windowing mode.
+     * </ul>
+     */
+    private boolean isTreatmentEnabledForActivity(@NonNull ActivityRecord activity) {
+        int orientation = activity.getRequestedConfigurationOrientation();
+        return shouldApplyFreeformTreatmentForCameraCompat(activity)
+                && mCameraStateMonitor.isCameraRunningForActivity(activity)
+                && orientation != ORIENTATION_UNDEFINED
+                && activity.inFreeformWindowingMode()
+                // "locked" and "nosensor" values are often used by camera apps that can't
+                // handle dynamic changes so we shouldn't force-letterbox them.
+                && activity.getRequestedOrientation() != SCREEN_ORIENTATION_NOSENSOR
+                && activity.getRequestedOrientation() != SCREEN_ORIENTATION_LOCKED
+                // TODO(b/332665280): investigate whether we can support activity embedding.
+                && !activity.isEmbedded();
+    }
+
+    private boolean isActivityForCameraIdRefreshing(@NonNull String cameraId) {
+        final ActivityRecord topActivity = mDisplayContent.topRunningActivity(
+                /* considerKeyguardState= */ true);
+        if (topActivity == null || !isTreatmentEnabledForActivity(topActivity)
+                || mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) {
+            return false;
+        }
+        return topActivity.mLetterboxUiController.isRefreshRequested();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
index 125eb2a..be44629 100644
--- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
+++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java
@@ -194,6 +194,16 @@
             final Rect startBounds = new Rect(0, 0, mDisplayContent.mInitialDisplayWidth,
                     mDisplayContent.mInitialDisplayHeight);
             final int fromRotation = mDisplayContent.getRotation();
+            if (Flags.blastSyncNotificationShadeOnDisplaySwitch() && physicalDisplayUpdated) {
+                final WindowState notificationShade =
+                        mDisplayContent.getDisplayPolicy().getNotificationShade();
+                if (notificationShade != null && notificationShade.isVisible()
+                        && mDisplayContent.mAtmService.mKeyguardController.isKeyguardOrAodShowing(
+                                mDisplayContent.mDisplayId)) {
+                    Slog.i(TAG, notificationShade + " uses blast for display switch");
+                    notificationShade.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
+                }
+            }
 
             mDisplayContent.mAtmService.deferWindowLayout();
             try {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a3a6b51..ad711cb 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -263,6 +263,7 @@
 import com.android.server.wm.utils.RegionUtils;
 import com.android.server.wm.utils.RotationCache;
 import com.android.server.wm.utils.WmDisplayCutout;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -477,6 +478,8 @@
     @Nullable
     final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
     @Nullable
+    final CameraCompatFreeformPolicy mCameraCompatFreeformPolicy;
+    @Nullable
     final CameraStateMonitor mCameraStateMonitor;
     @Nullable
     final ActivityRefresher mActivityRefresher;
@@ -683,7 +686,6 @@
      */
     private InputTarget mLastImeInputTarget;
 
-
     /**
      * Tracks the windowToken of the input method input target and the corresponding
      * {@link WindowContainerListener} for monitoring changes (e.g. the requested visibility
@@ -1233,11 +1235,26 @@
         // without the need to restart the device.
         final boolean shouldCreateDisplayRotationCompatPolicy =
                 mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime();
-        if (shouldCreateDisplayRotationCompatPolicy) {
+        final boolean shouldCreateCameraCompatFreeformPolicy = Flags.cameraCompatForFreeform()
+                && DesktopModeLaunchParamsModifier.canEnterDesktopMode(mWmService.mContext);
+        if (shouldCreateDisplayRotationCompatPolicy || shouldCreateCameraCompatFreeformPolicy) {
             mCameraStateMonitor = new CameraStateMonitor(this, mWmService.mH);
             mActivityRefresher = new ActivityRefresher(mWmService, mWmService.mH);
-            mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(
-                    this, mCameraStateMonitor, mActivityRefresher);
+            if (shouldCreateDisplayRotationCompatPolicy) {
+                mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(this,
+                        mCameraStateMonitor, mActivityRefresher);
+                mDisplayRotationCompatPolicy.start();
+            } else {
+                mDisplayRotationCompatPolicy = null;
+            }
+
+            if (shouldCreateCameraCompatFreeformPolicy) {
+                mCameraCompatFreeformPolicy = new CameraCompatFreeformPolicy(this,
+                        mCameraStateMonitor, mActivityRefresher);
+                mCameraCompatFreeformPolicy.start();
+            } else {
+                mCameraCompatFreeformPolicy = null;
+            }
 
             mCameraStateMonitor.startListeningToCameraState();
         } else {
@@ -1245,9 +1262,9 @@
             mCameraStateMonitor = null;
             mActivityRefresher = null;
             mDisplayRotationCompatPolicy = null;
+            mCameraCompatFreeformPolicy = null;
         }
 
-
         mRotationReversionController = new DisplayRotationReversionController(this);
 
         mInputMonitor = new InputMonitor(mWmService, this);
@@ -3350,6 +3367,11 @@
         if (mDisplayRotationCompatPolicy != null) {
             mDisplayRotationCompatPolicy.dispose();
         }
+
+        if (mCameraCompatFreeformPolicy != null) {
+            mCameraCompatFreeformPolicy.dispose();
+        }
+
         if (mCameraStateMonitor != null) {
             mCameraStateMonitor.dispose();
         }
@@ -5719,15 +5741,21 @@
      * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
      */
     boolean supportsSystemDecorations() {
+        boolean forceDesktopModeOnDisplay = forceDesktopMode();
+
+        if (com.android.window.flags.Flags.rearDisplayDisableForceDesktopSystemDecorations()) {
+            // System decorations should not be forced on a rear display due to security policies.
+            forceDesktopModeOnDisplay =
+                    forceDesktopModeOnDisplay && ((mDisplay.getFlags() & Display.FLAG_REAR) == 0);
+        }
+
         return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)
                 || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0
-                || forceDesktopMode())
+                || forceDesktopModeOnDisplay)
                 // VR virtual display will be used to run and render 2D app within a VR experience.
                 && mDisplayId != mWmService.mVr2dDisplayId
                 // Do not show system decorations on untrusted virtual display.
-                && isTrusted()
-                // No system decoration on rear display.
-                && (mDisplay.getFlags() & Display.FLAG_REAR) == 0;
+                && isTrusted();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2f23955..4a59fc2 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -128,6 +128,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.policy.ForceShowNavBarSettingsObserver;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -673,6 +674,7 @@
                 mService.mHighRefreshRateDenylist);
 
         mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler,
+                BackgroundThread.getHandler(),
                 mContext, () -> {
             synchronized (mLock) {
                 onConfigurationChanged();
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index e0cc064..6ecafdb 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -80,8 +80,11 @@
         mDisplayContent = displayContent;
         mWmService = displayContent.mWmService;
         mCameraStateMonitor = cameraStateMonitor;
-        mCameraStateMonitor.addCameraStateListener(this);
         mActivityRefresher = activityRefresher;
+    }
+
+    void start() {
+        mCameraStateMonitor.addCameraStateListener(this);
         mActivityRefresher.addEvaluator(this);
     }
 
@@ -365,7 +368,7 @@
     }
 
     // TODO(b/336474959): Do we need cameraId here?
-    private boolean isActivityForCameraIdRefreshing(String cameraId) {
+    private boolean isActivityForCameraIdRefreshing(@NonNull String cameraId) {
         final ActivityRecord topActivity = mDisplayContent.topRunningActivity(
                 /* considerKeyguardState= */ true);
         if (!isTreatmentEnabledForActivity(topActivity)
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 0978cb4..a21ba26 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -77,12 +77,14 @@
             mWindows.put(inputToken, window);
             final InputTransferToken inputTransferToken = window.getInputTransferToken();
             mWindowsByInputTransferToken.put(inputTransferToken, window);
-            mWindowsByWindowToken.put(window.getWindowToken(), window);
+            final IBinder windowToken = window.getWindowToken();
+            mWindowsByWindowToken.put(windowToken, window);
             updateProcessController(window);
             window.mClient.linkToDeath(()-> {
                 synchronized (mGlobalLock) {
                     mWindows.remove(inputToken);
                     mWindowsByInputTransferToken.remove(inputTransferToken);
+                    mWindowsByWindowToken.remove(windowToken);
                 }
             }, 0);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index d957591..cc2249de 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -59,6 +59,10 @@
 31002 wm_task_moved (TaskId|1|5),(Root Task ID|1|5),(Display Id|1|5),(ToTop|1),(Index|1)
 # Task removed with source explanation.
 31003 wm_task_removed (TaskId|1|5),(Root Task ID|1|5),(Display Id|1|5),(Reason|3)
+# Embedded TaskFragment created
+31004 wm_tf_created (Token|1|5),(TaskId|1|5)
+# Embedded TaskFragment removed
+31005 wm_tf_removed (Token|1|5),(TaskId|1|5)
 
 # Set the requested orientation of an activity.
 31006 wm_set_requested_orientation (Orientation|1|5),(Component Name|3)
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index b2ba9d1..bf99ccd 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
 
@@ -35,6 +36,7 @@
 import android.content.IntentFilter;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Binder;
 import android.os.Bundle;
@@ -60,6 +62,7 @@
 import android.view.animation.Interpolator;
 import android.widget.Button;
 import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
 
 import com.android.internal.R;
 
@@ -233,6 +236,7 @@
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                 PixelFormat.TRANSLUCENT);
         lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~Type.statusBars());
+        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         // Trusted overlay so touches outside the touchable area are allowed to pass through
         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
                 | WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
@@ -245,13 +249,20 @@
 
     private FrameLayout.LayoutParams getBubbleLayoutParams() {
         return new FrameLayout.LayoutParams(
-                mContext.getResources().getDimensionPixelSize(
-                        R.dimen.immersive_mode_cling_width),
+                getClingWindowWidth(),
                 ViewGroup.LayoutParams.WRAP_CONTENT,
                 Gravity.CENTER_HORIZONTAL | Gravity.TOP);
     }
 
     /**
+     * Returns the width of the cling window.
+     */
+    private int getClingWindowWidth() {
+        return mContext.getResources().getDimensionPixelSize(
+                R.dimen.immersive_mode_cling_width);
+    }
+
+    /**
      * @return the window token that's used by all ImmersiveModeConfirmation windows.
      */
     IBinder getWindowToken() {
@@ -387,6 +398,24 @@
 
         @Override
         public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+            // If the top display cutout overlaps with the full-width (windowWidth=-1)/centered
+            // dialog, then adjust the dialog contents by the cutout
+            final int width = getWidth();
+            final int windowWidth = getClingWindowWidth();
+            final Rect topDisplayCutout = insets.getDisplayCutout() != null
+                    ? insets.getDisplayCutout().getBoundingRectTop()
+                    : new Rect();
+            final boolean intersectsTopCutout = topDisplayCutout.intersects(
+                    width - (windowWidth / 2), 0,
+                    width + (windowWidth / 2), topDisplayCutout.bottom);
+            if (mClingWindow != null &&
+                    (windowWidth < 0 || (width > 0 && intersectsTopCutout))) {
+                final View iconView = mClingWindow.findViewById(R.id.immersive_cling_icon);
+                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
+                        iconView.getLayoutParams();
+                lp.topMargin = topDisplayCutout.bottom;
+                iconView.setLayoutParams(lp);
+            }
             // we will be hiding the nav bar, so layout as if it's already hidden
             return new WindowInsets.Builder(insets).setInsets(
                     Type.systemBars(), Insets.NONE).build();
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 3c556bf..6288a42 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -424,7 +424,9 @@
                 // Use task bounds to calculating rounded corners if the task is not floating.
                 final InsetsState state = copyState ? new InsetsState(originalState)
                         : originalState;
-                state.setRoundedCornerFrame(task.getBounds());
+                state.setRoundedCornerFrame(token.isFixedRotationTransforming()
+                        ? token.getFixedRotationTransformDisplayBounds()
+                        : task.getBounds());
                 return state;
             }
         }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index c683d4d..f70d2a5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -538,13 +538,6 @@
                 || !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
     }
 
-    /**
-     * @return Whether the dream activity is on top of default display.
-     */
-    boolean isShowingDream() {
-        return getDisplayState(DEFAULT_DISPLAY).mShowingDream;
-    }
-
     private void updateKeyguardSleepToken() {
         for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
              displayNdx >= 0; displayNdx--) {
@@ -631,7 +624,6 @@
          * Note that this can be true even if the keyguard is disabled or not showing.
          */
         private boolean mOccluded;
-        private boolean mShowingDream;
 
         private ActivityRecord mTopOccludesActivity;
         private ActivityRecord mDismissingKeyguardActivity;
@@ -669,7 +661,6 @@
 
             mRequestDismissKeyguard = false;
             mOccluded = false;
-            mShowingDream = false;
 
             mTopOccludesActivity = null;
             mDismissingKeyguardActivity = null;
@@ -697,21 +688,18 @@
 
                 // Only the top activity may control occluded, as we can't occlude the Keyguard
                 // if the top app doesn't want to occlude it.
-                occludedByActivity = mTopOccludesActivity != null
+                mOccluded = mTopOccludesActivity != null
                         || (mDismissingKeyguardActivity != null
                         && task.topRunningActivity() == mDismissingKeyguardActivity
                         && controller.canShowWhileOccluded(
                                 true /* dismissKeyguard */, false /* showWhenLocked */));
                 // FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD only apply for secondary display.
                 if (mDisplayId != DEFAULT_DISPLAY) {
-                    occludedByActivity |= display.canShowWithInsecureKeyguard()
+                    mOccluded |= display.canShowWithInsecureKeyguard()
                             && controller.canDismissKeyguard();
                 }
             }
 
-            mShowingDream = display.getDisplayPolicy().isShowingDreamLw() && (top != null
-                    && top.getActivityType() == ACTIVITY_TYPE_DREAM);
-            mOccluded = mShowingDream || occludedByActivity;
             mRequestDismissKeyguard = lastDismissKeyguardActivity != mDismissingKeyguardActivity
                     && !mOccluded && !mKeyguardGoingAway
                     && mDismissingKeyguardActivity != null;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index b8d0694..14a0467 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
@@ -40,7 +41,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
 import static android.content.pm.ActivityInfo.isFixedOrientation;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
@@ -54,10 +54,6 @@
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
 import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
-import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -80,7 +76,6 @@
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
-import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
 import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM;
 import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT;
@@ -109,6 +104,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager.TaskDescription;
+import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
 import android.content.pm.ActivityInfo.ScreenOrientation;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
@@ -135,12 +131,7 @@
 import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
 import java.util.function.BooleanSupplier;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
 
 /** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
 // TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
@@ -150,13 +141,8 @@
 // TODO(b/263021211): Consider renaming to more generic CompatUIController.
 final class LetterboxUiController {
 
-    private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE =
-            ActivityRecord::occludesParent;
-
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
 
-    private static final float UNDEFINED_ASPECT_RATIO = 0f;
-
     // Minimum value of mSetOrientationRequestCounter before qualifying as orientation request loop
     @VisibleForTesting
     static final int MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP = 2;
@@ -188,10 +174,6 @@
     // Corresponds to OVERRIDE_RESPECT_REQUESTED_ORIENTATION
     private final boolean mIsOverrideRespectRequestedOrientationEnabled;
 
-    // The list of observers for the destroy event of candidate opaque activities
-    // when dealing with translucent activities.
-    private final List<LetterboxUiController> mDestroyListeners = new ArrayList<>();
-
     @NonNull
     private final OptProp mAllowOrientationOverrideOptProp;
     @NonNull
@@ -206,34 +188,11 @@
     @NonNull
     private final OptProp mAllowUserAspectRatioFullscreenOverrideOptProp;
 
-    /*
-     * WindowContainerListener responsible to make translucent activities inherit
-     * constraints from the first opaque activity beneath them. It's null for not
-     * translucent activities.
-     */
-    @Nullable
-    private WindowContainerListener mLetterboxConfigListener;
-
-    @Nullable
-    @VisibleForTesting
-    ActivityRecord mFirstOpaqueActivityBeneath;
-
     private boolean mShowWallpaperForLetterboxBackground;
 
-    // In case of transparent activities we might need to access the aspectRatio of the
-    // first opaque activity beneath.
-    private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
-    private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
-
     // Updated when ActivityRecord#setRequestedOrientation is called
     private long mTimeMsLastSetOrientationRequest = 0;
 
-    @Configuration.Orientation
-    private int mInheritedOrientation = ORIENTATION_UNDEFINED;
-
-    // The app compat state for the opaque activity if any
-    private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
-
     // Counter for ActivityRecord#setRequestedOrientation
     private int mSetOrientationRequestCounter = 0;
 
@@ -242,9 +201,6 @@
     @PackageManager.UserMinAspectRatio
     private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET;
 
-    // The CompatDisplayInsets of the opaque activity beneath the translucent one.
-    private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
-
     @Nullable
     private Letterbox mLetterbox;
 
@@ -277,6 +233,9 @@
 
     private boolean mDoubleTapEvent;
 
+    @FreeformCameraCompatMode
+    private int mFreeformCameraCompatMode = CAMERA_COMPAT_FREEFORM_NONE;
+
     LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
         mLetterboxConfiguration = wmService.mLetterboxConfiguration;
         // Given activityRecord may not be fully constructed since LetterboxUiController
@@ -361,14 +320,7 @@
             mLetterbox.destroy();
             mLetterbox = null;
         }
-        for (int i = mDestroyListeners.size() - 1; i >= 0; i--) {
-            mDestroyListeners.get(i).updateInheritedLetterbox();
-        }
-        mDestroyListeners.clear();
-        if (mLetterboxConfigListener != null) {
-            mLetterboxConfigListener.onRemoved();
-            mLetterboxConfigListener = null;
-        }
+        mActivityRecord.mTransparentPolicy.stop();
     }
 
     void onMovedToDisplay(int displayId) {
@@ -609,7 +561,13 @@
         final DisplayContent displayContent = mActivityRecord.mDisplayContent;
         final boolean isIgnoreOrientationRequestEnabled = displayContent != null
                 && displayContent.getIgnoreOrientationRequest();
-        if (shouldApplyUserFullscreenOverride() && isIgnoreOrientationRequestEnabled) {
+        if (shouldApplyUserFullscreenOverride() && isIgnoreOrientationRequestEnabled
+                // Do not override orientation to fullscreen for camera activities.
+                // Fixed-orientation activities are rarely tested in other orientations, and it
+                // often results in sideways or stretched previews. As the camera compat treatment
+                // targets fixed-orientation activities, overriding the orientation disables the
+                // treatment.
+                && !mActivityRecord.isCameraActive()) {
             Slog.v(TAG, "Requested orientation " + screenOrientationToString(candidate) + " for "
                     + mActivityRecord + " is overridden to "
                     + screenOrientationToString(SCREEN_ORIENTATION_USER)
@@ -644,7 +602,13 @@
         // mUserAspectRatio is always initialized first in shouldApplyUserFullscreenOverride(),
         // which will always come first before this check as user override > device
         // manufacturer override.
-        if (isSystemOverrideToFullscreenEnabled() && isIgnoreOrientationRequestEnabled) {
+        if (isSystemOverrideToFullscreenEnabled() && isIgnoreOrientationRequestEnabled
+                // Do not override orientation to fullscreen for camera activities.
+                // Fixed-orientation activities are rarely tested in other orientations, and it
+                // often results in sideways or stretched previews. As the camera compat treatment
+                // targets fixed-orientation activities, overriding the orientation disables the
+                // treatment.
+                && !mActivityRecord.isCameraActive()) {
             Slog.v(TAG, "Requested orientation  " + screenOrientationToString(candidate) + " for "
                     + mActivityRecord + " is overridden to "
                     + screenOrientationToString(SCREEN_ORIENTATION_USER));
@@ -764,6 +728,15 @@
                         .isTreatmentEnabledForActivity(mActivityRecord);
     }
 
+    @FreeformCameraCompatMode
+    int getFreeformCameraCompatMode() {
+        return mFreeformCameraCompatMode;
+    }
+
+    void setFreeformCameraCompatMode(@FreeformCameraCompatMode int freeformCameraCompatMode) {
+        mFreeformCameraCompatMode = freeformCameraCompatMode;
+    }
+
     private boolean isCompatChangeEnabled(long overrideChangeId) {
         return mActivityRecord.info.isChangeEnabled(overrideChangeId);
     }
@@ -877,7 +850,7 @@
             // For this reason we use ActivityRecord#getBounds() that the translucent activity
             // inherits from the first opaque activity beneath and also takes care of the scaling
             // in case of activities in size compat mode.
-            final Rect innerFrame = hasInheritedLetterboxBehavior()
+            final Rect innerFrame = mActivityRecord.mTransparentPolicy.isRunning()
                     ? mActivityRecord.getBounds() : w.getFrame();
             mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
             if (mDoubleTapEvent) {
@@ -924,12 +897,14 @@
     // Check if we are in the given pose and in fullscreen mode.
     // Note that we check the task rather than the parent as with ActivityEmbedding the parent might
     // be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
-    // actually fullscreen.
+    // actually fullscreen. If display is still in transition e.g. unfolding, don't return true
+    // for HALF_FOLDED state or app will flicker.
     private boolean isDisplayFullScreenAndInPosture(boolean isTabletop) {
         Task task = mActivityRecord.getTask();
         return mActivityRecord.mDisplayContent != null && task != null
                 && mActivityRecord.mDisplayContent.getDisplayRotation().isDeviceInPosture(
                         DeviceStateController.DeviceState.HALF_FOLDED, isTabletop)
+                && !mActivityRecord.mDisplayContent.inTransition()
                 && task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
     }
 
@@ -1149,17 +1124,6 @@
     }
 
     boolean shouldApplyUserFullscreenOverride() {
-        // Do not override orientation to fullscreen for camera activities.
-        // Fixed-orientation activities are rarely tested in other orientations, and it often
-        // results in sideways or stretched previews. As the camera compat treatment targets
-        // fixed-orientation activities, overriding the orientation disables the treatment.
-        final DisplayContent displayContent = mActivityRecord.mDisplayContent;
-        if (displayContent != null && displayContent.mDisplayRotationCompatPolicy != null
-                && displayContent.mDisplayRotationCompatPolicy
-                .isCameraActive(mActivityRecord, /* mustBeFullscreen= */ true)) {
-            return false;
-        }
-
         if (isUserFullscreenOverrideEnabled()) {
             mUserAspectRatio = getUserMinAspectRatioOverrideCode();
 
@@ -1177,7 +1141,8 @@
     }
 
     boolean hasFullscreenOverride() {
-        return isSystemOverrideToFullscreenEnabled() || shouldApplyUserFullscreenOverride();
+        // `mUserAspectRatio` is always initialized first in `shouldApplyUserFullscreenOverride()`.
+        return shouldApplyUserFullscreenOverride() || isSystemOverrideToFullscreenEnabled();
     }
 
     float getUserMinAspectRatio() {
@@ -1341,19 +1306,20 @@
         if (!allowHorizontalReachabilityForThinLetterbox()) {
             return false;
         }
+        final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride();
+        final Rect parentAppBounds = parentAppBoundsOverride != null
+                ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
         // Use screen resolved bounds which uses resolved bounds or size compat bounds
         // as activity bounds can sometimes be empty
-        final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior()
-                ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds()
-                : mActivityRecord.getScreenResolvedBounds();
+        final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy
+                .getFirstOpaqueActivity().map(ActivityRecord::getScreenResolvedBounds)
+                .orElse(mActivityRecord.getScreenResolvedBounds());
         return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()
                 && parentConfiguration.windowConfiguration.getWindowingMode()
                         == WINDOWING_MODE_FULLSCREEN
                 // Check whether the activity fills the parent vertically.
-                && parentConfiguration.windowConfiguration.getAppBounds().height()
-                        <= opaqueActivityBounds.height()
-                && parentConfiguration.windowConfiguration.getAppBounds().width()
-                        > opaqueActivityBounds.width();
+                && parentAppBounds.height() <= opaqueActivityBounds.height()
+                && parentAppBounds.width() > opaqueActivityBounds.width();
     }
 
     @VisibleForTesting
@@ -1379,19 +1345,20 @@
         if (!allowVerticalReachabilityForThinLetterbox()) {
             return false;
         }
+        final Rect parentAppBoundsOverride = mActivityRecord.getParentAppBoundsOverride();
+        final Rect parentAppBounds = parentAppBoundsOverride != null
+                ? parentAppBoundsOverride : parentConfiguration.windowConfiguration.getAppBounds();
         // Use screen resolved bounds which uses resolved bounds or size compat bounds
-        // as activity bounds can sometimes be empty
-        final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior()
-                ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds()
-                : mActivityRecord.getScreenResolvedBounds();
+        // as activity bounds can sometimes be empty.
+        final Rect opaqueActivityBounds = mActivityRecord.mTransparentPolicy
+                .getFirstOpaqueActivity().map(ActivityRecord::getScreenResolvedBounds)
+                .orElse(mActivityRecord.getScreenResolvedBounds());
         return mLetterboxConfiguration.getIsVerticalReachabilityEnabled()
                 && parentConfiguration.windowConfiguration.getWindowingMode()
                         == WINDOWING_MODE_FULLSCREEN
                 // Check whether the activity fills the parent horizontally.
-                && parentConfiguration.windowConfiguration.getAppBounds().width()
-                        <= opaqueActivityBounds.width()
-                && parentConfiguration.windowConfiguration.getAppBounds().height()
-                        > opaqueActivityBounds.height();
+                && parentAppBounds.width() <= opaqueActivityBounds.width()
+                && parentAppBounds.height() > opaqueActivityBounds.height();
     }
 
     @VisibleForTesting
@@ -1490,7 +1457,8 @@
         // corners because we assume the specific layout would. This is the case when the layout
         // of the translucent activity uses only a part of all the bounds because of the use of
         // LayoutParams.WRAP_CONTENT.
-        if (hasInheritedLetterboxBehavior() && (cropBounds.width() != mainWindow.mRequestedWidth
+        if (mActivityRecord.mTransparentPolicy.isRunning()
+                && (cropBounds.width() != mainWindow.mRequestedWidth
                 || cropBounds.height() != mainWindow.mRequestedHeight)) {
             return null;
         }
@@ -1794,173 +1762,6 @@
         );
     }
 
-    /**
-     * Handles translucent activities letterboxing inheriting constraints from the
-     * first opaque activity beneath.
-     * @param parent The parent container.
-     */
-    void updateInheritedLetterbox() {
-        final WindowContainer<?> parent = mActivityRecord.getParent();
-        if (parent == null) {
-            return;
-        }
-        if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
-            return;
-        }
-        if (mLetterboxConfigListener != null) {
-            mLetterboxConfigListener.onRemoved();
-            clearInheritedConfig();
-        }
-        // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the
-        // opaque activity constraints because we're expecting the activity is already letterboxed.
-        mFirstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
-                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
-                mActivityRecord /* boundary */, false /* includeBoundary */,
-                true /* traverseTopToBottom */);
-        if (mFirstOpaqueActivityBeneath == null || mFirstOpaqueActivityBeneath.isEmbedded()) {
-            // We skip letterboxing if the translucent activity doesn't have any opaque
-            // activities beneath or the activity below is embedded which never has letterbox.
-            mActivityRecord.recomputeConfiguration();
-            return;
-        }
-        if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
-                || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
-            return;
-        }
-        mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.add(this);
-        inheritConfiguration(mFirstOpaqueActivityBeneath);
-        mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
-                mActivityRecord, mFirstOpaqueActivityBeneath,
-                (opaqueConfig, transparentOverrideConfig) -> {
-                    resetTranslucentOverrideConfig(transparentOverrideConfig);
-                    final Rect parentBounds = parent.getWindowConfiguration().getBounds();
-                    final Rect bounds = transparentOverrideConfig.windowConfiguration.getBounds();
-                    final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds();
-                    // We cannot use letterboxBounds directly here because the position relies on
-                    // letterboxing. Using letterboxBounds directly, would produce a double offset.
-                    bounds.set(parentBounds.left, parentBounds.top,
-                            parentBounds.left + letterboxBounds.width(),
-                            parentBounds.top + letterboxBounds.height());
-                    // We need to initialize appBounds to avoid NPE. The actual value will
-                    // be set ahead when resolving the Configuration for the activity.
-                    transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect());
-                    inheritConfiguration(mFirstOpaqueActivityBeneath);
-                    return transparentOverrideConfig;
-                });
-    }
-
-    /**
-     * @return {@code true} if the current activity is translucent with an opaque activity
-     * beneath. In this case it will inherit bounds, orientation and aspect ratios from
-     * the first opaque activity beneath.
-     */
-    boolean hasInheritedLetterboxBehavior() {
-        return mLetterboxConfigListener != null;
-    }
-
-    /**
-     * @return {@code true} if the current activity is translucent with an opaque activity
-     * beneath and needs to inherit its orientation.
-     */
-    boolean hasInheritedOrientation() {
-        // To force a different orientation, the transparent one needs to have an explicit one
-        // otherwise the existing one is fine and the actual orientation will depend on the
-        // bounds.
-        // To avoid wrong behaviour, we're not forcing orientation for activities with not
-        // fixed orientation (e.g. permission dialogs).
-        return hasInheritedLetterboxBehavior()
-                && mActivityRecord.getOverrideOrientation()
-                        != SCREEN_ORIENTATION_UNSPECIFIED;
-    }
-
-    float getInheritedMinAspectRatio() {
-        return mInheritedMinAspectRatio;
-    }
-
-    float getInheritedMaxAspectRatio() {
-        return mInheritedMaxAspectRatio;
-    }
-
-    int getInheritedAppCompatState() {
-        return mInheritedAppCompatState;
-    }
-
-    @Configuration.Orientation
-    int getInheritedOrientation() {
-        return mInheritedOrientation;
-    }
-
-    ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() {
-        return mInheritedCompatDisplayInsets;
-    }
-
-    void clearInheritedCompatDisplayInsets() {
-        mInheritedCompatDisplayInsets = null;
-    }
-
-    /**
-     * In case of translucent activities, it consumes the {@link ActivityRecord} of the first opaque
-     * activity beneath using the given consumer and returns {@code true}.
-     */
-    boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) {
-        return findOpaqueNotFinishingActivityBelow()
-                .map(activityRecord -> {
-                    consumer.accept(activityRecord);
-                    return true;
-                }).orElse(false);
-    }
-
-    /**
-     * @return The first not finishing opaque activity beneath the current translucent activity
-     * if it exists and the strategy is enabled.
-     */
-    Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() {
-        if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) {
-            return Optional.empty();
-        }
-        return Optional.ofNullable(mFirstOpaqueActivityBeneath);
-    }
-
-    /** Resets the screen size related fields so they can be resolved by requested bounds later. */
-    private static void resetTranslucentOverrideConfig(Configuration config) {
-        // The values for the following properties will be defined during the configuration
-        // resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the
-        // properties inherited from the first not finishing opaque activity beneath.
-        config.orientation = ORIENTATION_UNDEFINED;
-        config.screenWidthDp = config.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
-        config.screenHeightDp = config.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
-        config.smallestScreenWidthDp = config.compatSmallestScreenWidthDp =
-                SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
-    }
-
-    private void inheritConfiguration(ActivityRecord firstOpaque) {
-        // To avoid wrong behaviour, we're not forcing a specific aspect ratio to activities
-        // which are not already providing one (e.g. permission dialogs) and presumably also
-        // not resizable.
-        if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) {
-            mInheritedMinAspectRatio = firstOpaque.getMinAspectRatio();
-        }
-        if (mActivityRecord.getMaxAspectRatio() != UNDEFINED_ASPECT_RATIO) {
-            mInheritedMaxAspectRatio = firstOpaque.getMaxAspectRatio();
-        }
-        mInheritedOrientation = firstOpaque.getRequestedConfigurationOrientation();
-        mInheritedAppCompatState = firstOpaque.getAppCompatState();
-        mInheritedCompatDisplayInsets = firstOpaque.getCompatDisplayInsets();
-    }
-
-    private void clearInheritedConfig() {
-        if (mFirstOpaqueActivityBeneath != null) {
-            mFirstOpaqueActivityBeneath.mLetterboxUiController.mDestroyListeners.remove(this);
-        }
-        mFirstOpaqueActivityBeneath = null;
-        mLetterboxConfigListener = null;
-        mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
-        mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
-        mInheritedOrientation = ORIENTATION_UNDEFINED;
-        mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
-        mInheritedCompatDisplayInsets = null;
-    }
-
     @NonNull
     private static BooleanSupplier asLazy(@NonNull BooleanSupplier supplier) {
         return new BooleanSupplier() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 9c7c41c..f5ab38f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -314,13 +314,19 @@
         private boolean isDocument;
         private Uri documentData;
 
-        void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) {
+        // determines whether to include bubbled tasks. defaults to true to preserve previous
+        // behavior.
+        private boolean mIncludeLaunchedFromBubble = true;
+
+        void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
+                boolean includeLaunchedFromBubble) {
             mActivityType = activityType;
             mTaskAffinity = taskAffinity;
             mIntent = intent;
             mInfo = info;
             mIdealRecord = null;
             mCandidateRecord = null;
+            mIncludeLaunchedFromBubble = includeLaunchedFromBubble;
         }
 
         /**
@@ -362,7 +368,8 @@
             }
 
             // Overlays should not be considered as the task's logical top activity.
-            final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
+            final ActivityRecord r = task.getTopNonFinishingActivity(
+                    false /* includeOverlays */, mIncludeLaunchedFromBubble);
 
             if (r == null || r.finishing || r.mUserId != userId
                     || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
@@ -2370,18 +2377,20 @@
     }
 
     @Nullable
-    ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
+    ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea,
+            boolean includeLaunchedFromBubble) {
         return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info,
-                preferredTaskDisplayArea);
+                preferredTaskDisplayArea, includeLaunchedFromBubble);
     }
 
     @Nullable
     ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
-            TaskDisplayArea preferredTaskDisplayArea) {
+            TaskDisplayArea preferredTaskDisplayArea, boolean includeLaunchedFromBubble) {
         ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
-                        + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
-                preferredTaskDisplayArea);
-        mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);
+                        + ", info=%s, preferredTDA=%s, includeLaunchedFromBubble=%b", activityType,
+                taskAffinity, intent, info, preferredTaskDisplayArea, includeLaunchedFromBubble);
+        mTmpFindTaskResult.init(activityType, taskAffinity, intent, info,
+                includeLaunchedFromBubble);
 
         // Looking up task on preferred display area first
         ActivityRecord candidateActivity = null;
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1cc1a57..7510180 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -157,7 +157,7 @@
             // home & recent tasks
             return;
         }
-        if (task.isVisible()) {
+        if (task.isVisibleRequested()) {
             mTmpVisibleTasks.add(task);
         } else {
             mTmpInvisibleTasks.add(task);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index f2dc55f..b452131 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -140,7 +140,9 @@
     }
 
     private ActivityOptions cloneLaunchingOptions(ActivityOptions options) {
-        return options == null ? null : ActivityOptions.makeBasic()
+        if (options == null) return null;
+
+        final ActivityOptions cloneOptions = ActivityOptions.makeBasic()
                 .setLaunchTaskDisplayArea(options.getLaunchTaskDisplayArea())
                 .setLaunchDisplayId(options.getLaunchDisplayId())
                 .setCallerDisplayId(options.getCallerDisplayId())
@@ -150,6 +152,8 @@
                 .setPendingIntentCreatorBackgroundActivityStartMode(
                         options.getPendingIntentCreatorBackgroundActivityStartMode())
                 .setRemoteTransition(options.getRemoteTransition());
+        cloneOptions.setLaunchWindowingMode(options.getLaunchWindowingMode());
+        return cloneOptions;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 69a8aed..787c5d6 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -445,6 +445,7 @@
     int mPrevDisplayId = INVALID_DISPLAY;
 
     int mMultiWindowRestoreWindowingMode = INVALID_WINDOWING_MODE;
+    WindowContainerToken mMultiWindowRestoreParent;
 
     /**
      * Last requested orientation reported to DisplayContent. This is different from {@link
@@ -3514,7 +3515,10 @@
                 && !appCompatTaskInfo.topActivityInSizeCompat
                 && top.mLetterboxUiController.shouldEnableUserAspectRatioSettings()
                 && !info.isTopActivityTransparent;
-        appCompatTaskInfo.topActivityBoundsLetterboxed = top != null  && top.areBoundsLetterboxed();
+        appCompatTaskInfo.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
+        appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode = top == null
+                ? CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE
+                : top.mLetterboxUiController.getFreeformCameraCompatMode();
     }
 
     /**
@@ -3739,7 +3743,13 @@
         return !isOwnActivity && !isTrustedTaskFragment;
     }
 
-    void setDecorSurfaceBoosted(
+    /**
+     * Sets the requested boosted state for the decor surface.
+     *
+     * The caller must call {@link #commitDecorSurfaceBoostedState()} to ensure that the change is
+     * applied.
+     */
+    void requestDecorSurfaceBoosted(
             @NonNull TaskFragment ownerTaskFragment,
             boolean isBoosted,
             @Nullable SurfaceControl.Transaction clientTransaction) {
@@ -3747,9 +3757,17 @@
                 || mDecorSurfaceContainer.mOwnerTaskFragment != ownerTaskFragment) {
             return;
         }
-        mDecorSurfaceContainer.setBoosted(isBoosted, clientTransaction);
-        // scheduleAnimation() is called inside assignChildLayers(), which ensures that child
-        // surface visibility is updated with prepareSurfaces()
+        mDecorSurfaceContainer.requestBoosted(isBoosted, clientTransaction);
+    }
+
+    void commitDecorSurfaceBoostedState() {
+        if (mDecorSurfaceContainer == null) {
+            return;
+        }
+        mDecorSurfaceContainer.commitBoostedState();
+
+        // assignChildLayers() calls scheduleAnimation(), which calls prepareSurfaces()
+        // to ensure child surface visibility.
         assignChildLayers();
     }
 
@@ -4620,6 +4638,25 @@
         return TASK;
     }
 
+    /**
+     * Restores to the windowing mode saved when task requested to enter fullscreen using
+     * {@link Activity#requestFullscreenMode} API if it is valid. The task is also reparented to
+     * the previous parent if parent has changed.
+     */
+    void restoreWindowingMode() {
+        if (mMultiWindowRestoreWindowingMode == INVALID_WINDOWING_MODE) {
+            return;
+        }
+        if (!getParent().mRemoteToken.toWindowContainerToken()
+                .equals(mMultiWindowRestoreParent)) {
+            // Restore previous parent if parent has changed.
+            final Task parent = fromWindowContainerToken(mMultiWindowRestoreParent);
+            reparent(parent, MAX_VALUE);
+        }
+
+        setWindowingMode(mMultiWindowRestoreWindowingMode);
+    }
+
     @Override
     public void setWindowingMode(int windowingMode) {
         // Calling Task#setWindowingMode() for leaf task since this is a specialization of
@@ -4752,6 +4789,12 @@
                     if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement()
                             && lastParentBeforePip.mSyncState == SYNC_STATE_NONE) {
                         lastParentBeforePip.prepareSurfaces();
+                        // If the moveToFront is a part of finishing transition, then make sure
+                        // the z-order of tasks are up-to-date.
+                        if (topActivity.mTransitionController.inFinishingTransition(topActivity)) {
+                            Transition.assignLayers(taskDisplayArea,
+                                    taskDisplayArea.getPendingTransaction());
+                        }
                     }
                 }
                 if (isPip2ExperimentEnabled) {
@@ -6790,11 +6833,11 @@
      * Associates the decor surface with the given TF, or create one if there
      * isn't one in the Task yet. The surface will be removed with the TF,
      * and become invisible if the TF is invisible. */
-    void moveOrCreateDecorSurfaceFor(TaskFragment taskFragment) {
+    void moveOrCreateDecorSurfaceFor(TaskFragment taskFragment, boolean visible) {
         if (mDecorSurfaceContainer != null) {
             mDecorSurfaceContainer.mOwnerTaskFragment = taskFragment;
         } else {
-            mDecorSurfaceContainer = new DecorSurfaceContainer(taskFragment);
+            mDecorSurfaceContainer = new DecorSurfaceContainer(taskFragment, visible);
             assignChildLayers();
             sendTaskFragmentParentInfoChangedIfNeeded();
         }
@@ -6813,6 +6856,13 @@
         return mDecorSurfaceContainer != null ? mDecorSurfaceContainer.mDecorSurface : null;
     }
 
+    void setDecorSurfaceVisible(@NonNull SurfaceControl.Transaction t) {
+        if (mDecorSurfaceContainer == null) {
+            return;
+        }
+        t.show(mDecorSurfaceContainer.mDecorSurface);
+    }
+
     /**
      * A class managing the decor surface.
      *
@@ -6852,12 +6902,13 @@
         @NonNull TaskFragment mOwnerTaskFragment;
 
         private boolean mIsBoosted;
+        private boolean mIsBoostedRequested;
 
         // The surface transactions that will be applied when the layer is reassigned.
         @NonNull private final List<SurfaceControl.Transaction> mPendingClientTransactions =
                 new ArrayList<>();
 
-        private DecorSurfaceContainer(@NonNull TaskFragment initialOwner) {
+        private DecorSurfaceContainer(@NonNull TaskFragment initialOwner, boolean visible) {
             mOwnerTaskFragment = initialOwner;
             mContainerSurface = makeSurface().setContainerLayer()
                     .setParent(mSurfaceControl)
@@ -6870,23 +6921,36 @@
             mDecorSurface = makeSurface()
                     .setParent(mContainerSurface)
                     .setName(mSurfaceControl + " - decor surface")
-                    .setHidden(false)
+                    .setHidden(!visible)
                     .setCallsite("Task.DecorSurfaceContainer")
                     .build();
         }
 
-        private void setBoosted(
+        /**
+         * Sets the requested boosted state. The state is not applied until
+         * {@link commitBoostedState} is called.
+         */
+        private void requestBoosted(
                 boolean isBoosted, @Nullable SurfaceControl.Transaction clientTransaction) {
-            mIsBoosted = isBoosted;
-            // The client transaction will be applied together with the next assignLayer.
+            mIsBoostedRequested = isBoosted;
+            // The client transaction will be applied together with the next commitBoostedState.
             if (clientTransaction != null) {
                 mPendingClientTransactions.add(clientTransaction);
             }
         }
 
+        /** Applies the last requested boosted state. */
+        private void commitBoostedState() {
+            mIsBoosted = mIsBoostedRequested;
+            applyPendingClientTransactions(getSyncTransaction());
+        }
+
         private void assignLayer(@NonNull SurfaceControl.Transaction t, int layer) {
             t.setLayer(mContainerSurface, layer);
             t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible() || mIsBoosted);
+        }
+
+        private void applyPendingClientTransactions(@NonNull SurfaceControl.Transaction t) {
             for (int i = 0; i < mPendingClientTransactions.size(); i++) {
                 t.merge(mPendingClientTransactions.get(i));
             }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 61022cc..ab72e3c 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -40,6 +40,8 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
@@ -87,6 +89,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -1101,21 +1104,34 @@
     }
 
     ActivityRecord getTopNonFinishingActivity() {
-        return getTopNonFinishingActivity(true /* includeOverlays */);
+        return getTopNonFinishingActivity(
+                true /* includeOverlays */, true /* includeLaunchedFromBubble */);
     }
 
     /**
      * Returns the top-most non-finishing activity, even if the activity is NOT ok to show to
      * the current user.
      * @param includeOverlays whether the task overlay activity should be included.
+     * @param includeLaunchedFromBubble whether activities that were launched from a bubble should
+     *                                  be included.
      * @see #topRunningActivity(boolean)
      */
-    ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
-        // Split into 2 to avoid object creation due to variable capture.
+    ActivityRecord getTopNonFinishingActivity(boolean includeOverlays,
+            boolean includeLaunchedFromBubble) {
+        // Split to avoid object creation due to variable capture.
         if (includeOverlays) {
-            return getActivity((r) -> !r.finishing);
+            if (includeLaunchedFromBubble) {
+                return getActivity(r -> !r.finishing);
+            } else {
+                return getActivity(r -> !r.finishing && !r.getLaunchedFromBubble());
+            }
         }
-        return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
+        if (includeLaunchedFromBubble) {
+            return getActivity(r -> !r.finishing && !r.isTaskOverlay());
+        } else {
+            return getActivity(
+                    r -> !r.finishing && !r.isTaskOverlay() && !r.getLaunchedFromBubble());
+        }
     }
 
     ActivityRecord topRunningActivity() {
@@ -2225,7 +2241,43 @@
     static class ConfigOverrideHint {
         @Nullable DisplayInfo mTmpOverrideDisplayInfo;
         @Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets;
+        @Nullable Rect mTmpParentAppBoundsOverride;
+        int mTmpOverrideConfigOrientation;
         boolean mUseOverrideInsetsForConfig;
+
+        void resolveTmpOverrides(DisplayContent dc, Configuration parentConfig,
+                boolean isFixedRotationTransforming) {
+            mTmpParentAppBoundsOverride = new Rect(parentConfig.windowConfiguration.getAppBounds());
+            final Insets insets;
+            if (mUseOverrideInsetsForConfig && dc != null) {
+                // Insets are decoupled from configuration by default from V+, use legacy
+                // compatibility behaviour for apps targeting SDK earlier than 35
+                // (see applySizeOverrideIfNeeded).
+                int rotation = parentConfig.windowConfiguration.getRotation();
+                if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming) {
+                    rotation = dc.getRotation();
+                }
+                final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+                final int dw = rotated ? dc.mBaseDisplayHeight : dc.mBaseDisplayWidth;
+                final int dh = rotated ? dc.mBaseDisplayWidth : dc.mBaseDisplayHeight;
+                DisplayPolicy.DecorInsets.Info decorInsets = dc.getDisplayPolicy()
+                        .getDecorInsetsInfo(rotation, dw, dh);
+                final Rect stableBounds = decorInsets.mOverrideConfigFrame;
+                mTmpOverrideConfigOrientation = stableBounds.width() > stableBounds.height()
+                                ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+                insets = Insets.of(decorInsets.mOverrideNonDecorInsets);
+            } else {
+                insets = Insets.NONE;
+            }
+            mTmpParentAppBoundsOverride.inset(insets);
+        }
+
+        void resetTmpOverrides() {
+            mTmpOverrideDisplayInfo = null;
+            mTmpCompatInsets = null;
+            mTmpParentAppBoundsOverride = null;
+            mTmpOverrideConfigOrientation = ORIENTATION_UNDEFINED;
+        }
     }
 
     void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@@ -2311,7 +2363,9 @@
             if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) {
                 final Rect containingAppBounds;
                 if (insideParentBounds) {
-                    containingAppBounds = parentConfig.windowConfiguration.getAppBounds();
+                    containingAppBounds = useOverrideInsetsForConfig
+                            ? overrideHint.mTmpParentAppBoundsOverride
+                            : parentConfig.windowConfiguration.getAppBounds();
                 } else {
                     // Restrict appBounds to display non-decor rather than parent because the
                     // override bounds are beyond the parent. Otherwise, it won't match the
@@ -2999,6 +3053,9 @@
 
     @Override
     void removeImmediately() {
+        if (asTask() == null) {
+            EventLogTags.writeWmTfRemoved(System.identityHashCode(this), getTaskId());
+        }
         mIsRemovalRequested = false;
         resetAdjacentTaskFragment();
         cleanUpEmbeddedTaskFragment();
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 86c6f8d..bc45c70 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1048,7 +1048,7 @@
         // the animation played. This puts the layers back into the correct order.
         for (int i = displays.size() - 1; i >= 0; --i) {
             if (displays.valueAt(i) == null) continue;
-            updateDisplayLayers(displays.valueAt(i), t);
+            assignLayers(displays.valueAt(i), t);
         }
 
         for (int i = 0; i < info.getRootCount(); ++i) {
@@ -1056,12 +1056,13 @@
         }
     }
 
-    private static void updateDisplayLayers(DisplayContent dc, SurfaceControl.Transaction t) {
-        dc.mTransitionController.mBuildingFinishLayers = true;
+    /** Assigns the layers for the start or end state of transition. */
+    static void assignLayers(WindowContainer<?> wc, SurfaceControl.Transaction t) {
+        wc.mTransitionController.mBuildingFinishLayers = true;
         try {
-            dc.assignChildLayers(t);
+            wc.assignChildLayers(t);
         } finally {
-            dc.mTransitionController.mBuildingFinishLayers = false;
+            wc.mTransitionController.mBuildingFinishLayers = false;
         }
     }
 
@@ -1374,13 +1375,22 @@
         // processed all the participants first (in particular, we want to trigger pip-enter first)
         for (int i = 0; i < mParticipants.size(); ++i) {
             final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
+            if (ar == null) continue;
+
             // If the activity was just inserted to an invisible task, it will keep INITIALIZING
             // state. Then no need to notify the callback to avoid clearing some states
             // unexpectedly, e.g. launch-task-behind.
-            if (ar != null && (ar.isVisibleRequested()
-                    || !ar.isState(ActivityRecord.State.INITIALIZING))) {
+            if (ar.isVisibleRequested() || !ar.isState(ActivityRecord.State.INITIALIZING)) {
                 mController.dispatchLegacyAppTransitionFinished(ar);
             }
+
+            // Reset the ActivityRecord#mCurrentLaunchCanTurnScreenOn state if it is not the top
+            // running activity. Doing so in case the state is not yet consumed during rapid
+            // activity launch.
+            if (ar.currentLaunchCanTurnScreenOn() && ar.getDisplayContent() != null
+                    && ar.getDisplayContent().topRunningActivity() != ar) {
+                ar.setCurrentLaunchCanTurnScreenOn(false);
+            }
         }
 
         // Update the input-sink (touch-blocking) state now that the animation is finished.
@@ -1895,6 +1905,8 @@
             for (int i = changes.size() - 1; i >= 0; --i) {
                 if (mTargets.get(i).mContainer.asActivityRecord() != null) {
                     changes.get(i).setAnimationOptions(mOverrideOptions);
+                    // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions.
+                    changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor());
                 }
             }
         }
@@ -2706,7 +2718,7 @@
             rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
             // Update layers to start transaction because we prevent assignment during collect, so
             // the layer of transition root can be correct.
-            updateDisplayLayers(dc, startT);
+            assignLayers(dc, startT);
             startT.setLayer(rootLeash, leashReference.getLastLayer());
             outInfo.addRootLeash(endDisplayId, rootLeash,
                     ancestor.getBounds().left, ancestor.getBounds().top);
@@ -2804,6 +2816,9 @@
             final Rect parentBounds = parent.getBounds();
             change.setEndRelOffset(bounds.left - parentBounds.left,
                     bounds.top - parentBounds.top);
+            if (Flags.activityEmbeddingOverlayPresentationFlag()) {
+                change.setEndParentSize(parentBounds.width(), parentBounds.height());
+            }
             int endRotation = target.getWindowConfiguration().getRotation();
             if (activityRecord != null) {
                 // TODO(b/227427984): Shell needs to aware letterbox.
diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java
new file mode 100644
index 0000000..b408397
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TransparentPolicy.java
@@ -0,0 +1,353 @@
+/*
+ * 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.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * Encapsulate logic about translucent activities.
+ * <p/>
+ * An activity is defined as translucent if {@link ActivityRecord#fillsParent()} returns
+ * {@code false}. When the policy is running for a letterboxed activity, a transparent activity
+ * will inherit constraints about bounds, aspect ratios and orientation from the first not finishing
+ * activity below.
+ */
+class TransparentPolicy {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "TransparentPolicy" : TAG_ATM;
+
+    // The predicate used to find the first opaque not finishing activity below the potential
+    // transparent activity.
+    private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE =
+            ActivityRecord::occludesParent;
+
+    // The ActivityRecord this policy relates to.
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+
+    // If transparent activity policy is enabled.
+    @NonNull
+    private final BooleanSupplier mIsTranslucentLetterboxingEnabledSupplier;
+
+    // The list of observers for the destroy event of candidate opaque activities
+    // when dealing with translucent activities.
+    @NonNull
+    private final List<TransparentPolicy> mDestroyListeners = new ArrayList<>();
+
+    // The current state for the possible transparent activity
+    @NonNull
+    private final TransparentPolicyState mTransparentPolicyState;
+
+    TransparentPolicy(@NonNull ActivityRecord activityRecord,
+            @NonNull LetterboxConfiguration letterboxConfiguration) {
+        mActivityRecord = activityRecord;
+        mIsTranslucentLetterboxingEnabledSupplier =
+                letterboxConfiguration::isTranslucentLetterboxingEnabled;
+        mTransparentPolicyState = new TransparentPolicyState(activityRecord);
+    }
+
+    /**
+     * Handles translucent activities letterboxing inheriting constraints from the
+     * first opaque activity beneath.
+     */
+    void start() {
+        if (!mIsTranslucentLetterboxingEnabledSupplier.getAsBoolean()) {
+            return;
+        }
+        final WindowContainer<?> parent = mActivityRecord.getParent();
+        if (parent == null) {
+            return;
+        }
+        mTransparentPolicyState.reset();
+        // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the
+        // opaque activity constraints because we're expecting the activity is already letterboxed.
+        final ActivityRecord firstOpaqueActivity = mActivityRecord.getTask().getActivity(
+                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
+                mActivityRecord /* boundary */, false /* includeBoundary */,
+                true /* traverseTopToBottom */);
+        // We check if we need for some reason to skip the policy gievn the specific first
+        // opaque activity
+        if (shouldSkipTransparentPolicy(firstOpaqueActivity)) {
+            return;
+        }
+        mTransparentPolicyState.start(firstOpaqueActivity);
+    }
+
+    void stop() {
+        for (int i = mDestroyListeners.size() - 1; i >= 0; i--) {
+            mDestroyListeners.get(i).start();
+        }
+        mDestroyListeners.clear();
+        mTransparentPolicyState.reset();
+    }
+
+    /**
+     * @return {@code true} if the current activity is translucent with an opaque activity
+     * beneath and the related policy is running. In this case it will inherit bounds, orientation
+     * and aspect ratios from the first opaque activity beneath.
+     */
+    boolean isRunning() {
+        return mTransparentPolicyState.isRunning();
+    }
+
+    /**
+     * @return {@code true} if the current activity is translucent with an opaque activity
+     * beneath and needs to inherit its orientation.
+     */
+    boolean hasInheritedOrientation() {
+        // To avoid wrong behaviour (e.g. permission dialogs not centered or with wrong size),
+        // transparent activities inherit orientation from the first opaque activity below only if
+        // they explicitly define an orientation different from SCREEN_ORIENTATION_UNSPECIFIED.
+        return isRunning()
+                && mActivityRecord.getOverrideOrientation()
+                != SCREEN_ORIENTATION_UNSPECIFIED;
+    }
+
+    float getInheritedMinAspectRatio() {
+        return mTransparentPolicyState.mInheritedMinAspectRatio;
+    }
+
+    float getInheritedMaxAspectRatio() {
+        return mTransparentPolicyState.mInheritedMaxAspectRatio;
+    }
+
+    int getInheritedAppCompatState() {
+        return mTransparentPolicyState.mInheritedAppCompatState;
+    }
+
+    @Configuration.Orientation
+    int getInheritedOrientation() {
+        return mTransparentPolicyState.mInheritedOrientation;
+    }
+
+    ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() {
+        return mTransparentPolicyState.mInheritedCompatDisplayInsets;
+    }
+
+    void clearInheritedCompatDisplayInsets() {
+        mTransparentPolicyState.clearInheritedCompatDisplayInsets();
+    }
+
+    TransparentPolicyState getTransparentPolicyState() {
+        return mTransparentPolicyState;
+    }
+
+    /**
+     * In case of translucent activities, it consumes the {@link ActivityRecord} of the first opaque
+     * activity beneath using the given consumer and returns {@code true}.
+     */
+    boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) {
+        return mTransparentPolicyState.applyOnOpaqueActivityBelow(consumer);
+    }
+
+    @NonNull
+    Optional<ActivityRecord> getFirstOpaqueActivity() {
+        return isRunning() ? Optional.of(mTransparentPolicyState.mFirstOpaqueActivity)
+                : Optional.empty();
+    }
+
+    /**
+     * @return The first not finishing opaque activity beneath the current translucent activity
+     * if it exists and the strategy is enabled.
+     */
+    Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() {
+        return mTransparentPolicyState.findOpaqueNotFinishingActivityBelow();
+    }
+
+    // We evaluate the case when the policy should not be applied.
+    private boolean shouldSkipTransparentPolicy(@Nullable ActivityRecord opaqueActivity) {
+        if (opaqueActivity == null || opaqueActivity.isEmbedded()) {
+            // We skip letterboxing if the translucent activity doesn't have any
+            // opaque activities beneath or the activity below is embedded which
+            // never has letterbox.
+            mActivityRecord.recomputeConfiguration();
+            return true;
+        }
+        if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
+                || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
+            return true;
+        }
+        return false;
+    }
+
+    /** Resets the screen size related fields so they can be resolved by requested bounds later. */
+    private static void resetTranslucentOverrideConfig(Configuration config) {
+        // The values for the following properties will be defined during the configuration
+        // resolution in {@link ActivityRecord#resolveOverrideConfiguration} using the
+        // properties inherited from the first not finishing opaque activity beneath.
+        config.orientation = ORIENTATION_UNDEFINED;
+        config.screenWidthDp = config.compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
+        config.screenHeightDp = config.compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
+        config.smallestScreenWidthDp = config.compatSmallestScreenWidthDp =
+                SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+    }
+
+    private void inheritConfiguration(ActivityRecord firstOpaque) {
+        mTransparentPolicyState.inheritFromOpaque(firstOpaque);
+    }
+
+    /**
+     * Encapsulate the state for the current translucent activity when the transparent policy
+     * has started.
+     */
+    static class TransparentPolicyState {
+        // Aspect ratio value to consider as undefined.
+        private static final float UNDEFINED_ASPECT_RATIO = 0f;
+
+        @NonNull
+        private final ActivityRecord mActivityRecord;
+
+        @Configuration.Orientation
+        private int mInheritedOrientation = ORIENTATION_UNDEFINED;
+        private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
+        private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
+
+        // The app compat state for the opaque activity if any
+        private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
+
+        // The CompatDisplayInsets of the opaque activity beneath the translucent one.
+        @Nullable
+        private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
+
+        @Nullable
+        private ActivityRecord mFirstOpaqueActivity;
+
+        /*
+         * WindowContainerListener responsible to make translucent activities inherit
+         * constraints from the first opaque activity beneath them. It's null for not
+         * translucent activities.
+         */
+        @Nullable
+        private WindowContainerListener mLetterboxConfigListener;
+
+        TransparentPolicyState(@NonNull ActivityRecord activityRecord) {
+            mActivityRecord = activityRecord;
+        }
+
+        private void start(@NonNull ActivityRecord firstOpaqueActivity) {
+            mFirstOpaqueActivity = firstOpaqueActivity;
+            mFirstOpaqueActivity.mTransparentPolicy
+                    .mDestroyListeners.add(mActivityRecord.mTransparentPolicy);
+            inheritFromOpaque(firstOpaqueActivity);
+            final WindowContainer<?> parent = mActivityRecord.getParent();
+            mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation(
+                    mActivityRecord, mFirstOpaqueActivity,
+                    (opaqueConfig, transparentOverrideConfig) -> {
+                        resetTranslucentOverrideConfig(transparentOverrideConfig);
+                        final Rect parentBounds = parent.getWindowConfiguration().getBounds();
+                        final Rect bounds = transparentOverrideConfig
+                                .windowConfiguration.getBounds();
+                        final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds();
+                        // We cannot use letterboxBounds directly here because the position relies
+                        // on letterboxing. Using letterboxBounds directly, would produce a
+                        // double offset.
+                        bounds.set(parentBounds.left, parentBounds.top,
+                                parentBounds.left + letterboxBounds.width(),
+                                parentBounds.top + letterboxBounds.height());
+                        // We need to initialize appBounds to avoid NPE. The actual value will
+                        // be set ahead when resolving the Configuration for the activity.
+                        transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect());
+                        inheritFromOpaque(mFirstOpaqueActivity);
+                        return transparentOverrideConfig;
+                    });
+        }
+
+        private void inheritFromOpaque(@NonNull ActivityRecord opaqueActivity) {
+            // To avoid wrong behaviour, we're not forcing a specific aspect ratio to activities
+            // which are not already providing one (e.g. permission dialogs) and presumably also
+            // not resizable.
+            if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) {
+                mInheritedMinAspectRatio = opaqueActivity.getMinAspectRatio();
+            }
+            if (mActivityRecord.getMaxAspectRatio() != UNDEFINED_ASPECT_RATIO) {
+                mInheritedMaxAspectRatio = opaqueActivity.getMaxAspectRatio();
+            }
+            mInheritedOrientation = opaqueActivity.getRequestedConfigurationOrientation();
+            mInheritedAppCompatState = opaqueActivity.getAppCompatState();
+            mInheritedCompatDisplayInsets = opaqueActivity.getCompatDisplayInsets();
+        }
+
+        private void reset() {
+            if (mLetterboxConfigListener != null) {
+                mLetterboxConfigListener.onRemoved();
+            }
+            mLetterboxConfigListener = null;
+            mInheritedOrientation = ORIENTATION_UNDEFINED;
+            mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
+            mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
+            mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
+            mInheritedCompatDisplayInsets = null;
+            if (mFirstOpaqueActivity != null) {
+                mFirstOpaqueActivity.mTransparentPolicy
+                        .mDestroyListeners.remove(mActivityRecord.mTransparentPolicy);
+            }
+            mFirstOpaqueActivity = null;
+        }
+
+        private boolean isRunning() {
+            return mLetterboxConfigListener != null;
+        }
+
+        private void clearInheritedCompatDisplayInsets() {
+            mInheritedCompatDisplayInsets = null;
+        }
+
+        /**
+         * @return The first not finishing opaque activity beneath the current translucent activity
+         * if it exists and the strategy is enabled.
+         */
+        private Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() {
+            if (!isRunning() || mActivityRecord.getTask() == null) {
+                return Optional.empty();
+            }
+            return Optional.ofNullable(mFirstOpaqueActivity);
+        }
+
+        /**
+         * In case of translucent activities, it consumes the {@link ActivityRecord} of the first
+         * opaque activity beneath using the given consumer and returns {@code true}.
+         */
+        private boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) {
+            return findOpaqueNotFinishingActivityBelow()
+                    .map(activityRecord -> {
+                        consumer.accept(activityRecord);
+                        return true;
+                    }).orElse(false);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index edbba92..70143ba 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1077,9 +1077,6 @@
         if (dc != null && dc != this) {
             dc.getPendingTransaction().merge(mPendingTransaction);
         }
-        if (dc != this && mLocalInsetsSources != null) {
-            mLocalInsetsSources.clear();
-        }
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer child = mChildren.get(i);
             child.onDisplayChanged(dc);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8a6c73a..2b375e1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2027,7 +2027,9 @@
             // Otherwise, look at the package
             final ApplicationInfo appInfo = mPmInternal.getApplicationInfo(
                     packageName, 0 /* flags */, SYSTEM_UID, UserHandle.getUserId(callingUid));
-            if (appInfo == null || appInfo.uid != callingUid) {
+            if (appInfo == null
+                    || !mPmInternal.isSameApp(
+                            packageName, callingUid, UserHandle.getUserId(callingUid))) {
                 throw new SecurityException("Package " + packageName + " not in UID "
                         + callingUid);
             }
@@ -3502,10 +3504,11 @@
         if (!checkCallingPermission(permission.CONTROL_KEYGUARD, "dismissKeyguard")) {
             throw new SecurityException("Requires CONTROL_KEYGUARD permission");
         }
-        if (!dreamHandlesConfirmKeys() && mAtmService.mKeyguardController.isShowingDream()) {
-            mAtmService.mTaskSupervisor.wakeUp("leaveDream");
-        }
         synchronized (mGlobalLock) {
+            if (!dreamHandlesConfirmKeys()
+                    && getDefaultDisplayContentLocked().getDisplayPolicy().isShowingDreamLw()) {
+                mAtmService.mTaskSupervisor.wakeUp("leaveDream");
+            }
             mPolicy.dismissKeyguardLw(callback, message);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 7eda53f..72109d34 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -23,8 +23,8 @@
 import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
-import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE;
+import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_BOTTOM_OF_TASK;
@@ -1595,11 +1595,31 @@
                 break;
             }
             case OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE: {
-                taskFragment.getTask().moveOrCreateDecorSurfaceFor(taskFragment);
+                final Task task = taskFragment.getTask();
+                if (task == null) {
+                    break;
+                }
+                // If any TaskFragment in the Task is collected by the transition, we make the decor
+                // surface visible in sync with the TaskFragment transition. Otherwise, we make the
+                // decor surface visible immediately.
+                final TaskFragment syncTaskFragment = transition != null
+                        ? task.getTaskFragment(transition.mParticipants::contains)
+                        : null;
+
+                if (syncTaskFragment != null) {
+                    task.moveOrCreateDecorSurfaceFor(taskFragment, false /* visible */);
+                    task.setDecorSurfaceVisible(syncTaskFragment.getSyncTransaction());
+                } else {
+                    task.moveOrCreateDecorSurfaceFor(taskFragment, true /* visible */);
+                }
                 break;
             }
             case OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE: {
-                taskFragment.getTask().removeDecorSurface();
+                final Task task = taskFragment.getTask();
+                if (task == null) {
+                    break;
+                }
+                task.removeDecorSurface();
                 break;
             }
             case OP_TYPE_SET_DIM_ON_TASK: {
@@ -1627,21 +1647,15 @@
                         clientTransaction.sanitize(caller.mPid, caller.mUid);
                     }
 
-                    if (transition != null) {
-                        // The decor surface boost/unboost must happen after the transition is
-                        // completed. Otherwise, the decor surface could be moved before Shell
-                        // completes the transition, causing flicker.
-                        transition.addTransitionEndedListener(() ->
-                                task.setDecorSurfaceBoosted(
-                                        taskFragment,
-                                        operation.getBooleanValue() /* isBoosted */,
-                                        clientTransaction));
-                    } else {
-                        task.setDecorSurfaceBoosted(
-                                taskFragment,
-                                operation.getBooleanValue() /* isBoosted */,
-                                clientTransaction);
-                    }
+                    task.requestDecorSurfaceBoosted(
+                            taskFragment,
+                            operation.getBooleanValue() /* isBoosted */,
+                            clientTransaction);
+
+                    // The decor surface boost/unboost must be applied after the transition is
+                    // completed. Otherwise, the decor surface could be moved before Shell completes
+                    // the transition, causing flicker.
+                    runAfterTransition(transition, task::commitDecorSurfaceBoostedState);
                 }
                 break;
             }
@@ -1654,6 +1668,19 @@
         return effects;
     }
 
+    /**
+     * Executes the provided {@code runnable} after the {@code transition}. If the
+     * {@code transition} is {@code null}, the {@code runnable} is executed immediately.
+     */
+    private static void runAfterTransition(
+            @Nullable Transition transition, @NonNull Runnable runnable) {
+        if (transition == null) {
+            runnable.run();
+        } else {
+            transition.addTransitionEndedListener(runnable);
+        }
+    }
+
     private boolean validateTaskFragmentOperation(
             @NonNull WindowContainerTransaction.HierarchyOp hop,
             @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) {
@@ -1835,7 +1862,7 @@
 
             task.getParent().positionChildAt(
                     hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
-                    task, false /* includingParents */);
+                    task, hop.includingParents());
         }
         return TRANSACT_EFFECTS_LIFECYCLE;
     }
@@ -2361,6 +2388,7 @@
             position = POSITION_TOP;
         }
         ownerTask.addChild(taskFragment, position);
+        EventLogTags.writeWmTfCreated(System.identityHashCode(taskFragment), ownerTask.mTaskId);
         taskFragment.setWindowingMode(creationParams.getWindowingMode());
         if (!creationParams.getInitialRelativeBounds().isEmpty()) {
             // The surface operations for the task fragment should sync with the transition.
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 64cbb0d1..1667e27 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -3,6 +3,7 @@
 
 # Power
 per-file com_android_server_HardwarePropertiesManagerService.cpp = file:/services/core/java/com/android/server/power/OWNERS
+per-file com_android_server_power_* = file:/services/core/java/com/android/server/power/OWNERS
 
 # BatteryStats
 per-file com_android_server_am_BatteryStatsService.cpp = file:/BATTERY_STATS_OWNERS
@@ -21,7 +22,6 @@
 per-file com_android_server_net_* = file:/services/core/java/com/android/server/net/OWNERS
 per-file com_android_server_pdb_* = file:/services/core/java/com/android/server/pdb/OWNERS
 per-file com_android_server_pm_* = file:/services/core/java/com/android/server/pm/OWNERS
-per-file com_android_server_power_* = file:/services/core/java/com/android/server/power/OWNERS
 per-file com_android_server_powerstats_* = file:/services/core/java/com/android/server/powerstats/OWNERS
 per-file com_android_server_power_stats_* = file:/BATTERY_STATS_OWNERS
 per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b19de18..1f0c827 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -362,6 +362,7 @@
     void notifyDropWindow(const sp<IBinder>& token, float x, float y) override;
     void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
                                  const std::set<gui::Uid>& uids) override;
+    void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) override;
 
     /* --- PointerControllerPolicyInterface implementation --- */
 
@@ -1108,6 +1109,10 @@
     checkAndClearExceptionFromCallback(env, "notifyVibratorState");
 }
 
+void NativeInputManager::notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) {
+    mInputManager->getChoreographer().setFocusedDisplay(displayId);
+}
+
 void NativeInputManager::displayRemoved(JNIEnv* env, ui::LogicalDisplayId displayId) {
     mInputManager->getDispatcher().displayRemoved(displayId);
 }
diff --git a/services/core/jni/com_android_server_utils_AnrTimer.cpp b/services/core/jni/com_android_server_utils_AnrTimer.cpp
index f0bd037..2edf129 100644
--- a/services/core/jni/com_android_server_utils_AnrTimer.cpp
+++ b/services/core/jni/com_android_server_utils_AnrTimer.cpp
@@ -18,6 +18,7 @@
 #include <pthread.h>
 #include <sys/timerfd.h>
 #include <inttypes.h>
+#include <sys/stat.h>
 
 #include <algorithm>
 #include <list>
@@ -27,17 +28,20 @@
 #include <vector>
 
 #define LOG_TAG "AnrTimerService"
+#define ATRACE_TAG ATRACE_TAG_ACTIVITY_MANAGER
+#define ANR_TIMER_TRACK "AnrTimerTrack"
 
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 #include "core_jni_helpers.h"
 
+#include <processgroup/processgroup.h>
+#include <utils/Log.h>
 #include <utils/Mutex.h>
 #include <utils/Timers.h>
+#include <utils/Trace.h>
 
-#include <utils/Log.h>
-#include <utils/Timers.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
@@ -81,13 +85,55 @@
 // A local debug flag that gates a set of log messages for debug only.  This is normally const
 // false so the debug statements are not included in the image.  The flag can be set true in a
 // unit test image to debug test failures.
-const bool DEBUG = false;
+const bool DEBUG_TIMER = false;
+
+// A local debug flag to debug the timer thread itself.
+const bool DEBUG_TICKER = false;
+
+// Enable error logging.
+const bool DEBUG_ERROR = true;
 
 // Return the current time in nanoseconds.  This time is relative to system boot.
 nsecs_t now() {
     return systemTime(SYSTEM_TIME_MONOTONIC);
 }
 
+// Return true if the process exists and false if we cannot know.
+bool processExists(pid_t pid) {
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path), "/proc/%d", pid);
+    struct stat buff;
+    return stat(path, &buff) == 0;
+}
+
+// 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;
+        } else {
+            snprintf(buffer, sizeof(buffer), "err: %s", strerror(errno));
+        }
+    } else {
+        snprintf(buffer, sizeof(buffer), "notfound");
+    }
+    return std::string(buffer);
+}
+
 /**
  * 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
@@ -128,10 +174,11 @@
     /**
      * Create a timer service.  The service is initialized with a name used for logging.  The
      * constructor is also given the notifier callback, and two cookies for the callback: the
-     * traditional void* and an int.
+     * traditional void* and Java object pointer.  The remaining parameters are
+     * configuration options.
      */
     AnrTimerService(char const* label, notifier_t notifier, void* cookie, jweak jtimer, Ticker*,
-                    bool extend);
+                    bool extend, bool freeze);
 
     // Delete the service and clean up memory.
     ~AnrTimerService();
@@ -147,10 +194,9 @@
     // returns false.
     bool cancel(timer_id_t timerId);
 
-    // Accept a timer and remove it from all lists.  This is called when the upper layers accept
-    // that a timer has expired.  If the timer was Expired, the function returns true.  The
-    // other possibilities are tha the timer was Running or non-existing; in both cases, the
-    // function returns false.
+    // Accept a timer.  This is called when the upper layers accept that a timer has expired.
+    // If the timer was Expired and its process was frozen, the timer is pushed to the expired
+    // list and 'true' is returned.  Otherwise the function returns false.
     bool accept(timer_id_t timerId);
 
     // Discard a timer without collecting any statistics.  This is called when the upper layers
@@ -162,6 +208,9 @@
     // A timer has expired.
     void expire(timer_id_t);
 
+    // Release a timer.  The timer must be in the expired list.
+    bool release(timer_id_t);
+
     // Return the Java object associated with this instance.
     jweak jtimer() const {
         return notifierObject_;
@@ -172,16 +221,23 @@
 
   private:
     // The service cannot be copied.
-    AnrTimerService(AnrTimerService const &) = delete;
+    AnrTimerService(AnrTimerService const&) = delete;
 
     // Insert a timer into the running list.  The lock must be held by the caller.
-    void insert(const Timer&);
+    void insertLocked(const Timer&);
 
     // Remove a timer from the lists and return it. The lock must be held by the caller.
-    Timer remove(timer_id_t timerId);
+    Timer removeLocked(timer_id_t timerId);
+
+    // Add a timer to the expired list.
+    void addExpiredLocked(Timer const&);
+
+    // Scrub the expired list by removing all entries for non-existent processes.  The expired
+    // lock must be held by the caller.
+    void scrubExpiredLocked();
 
     // Return a string representation of a status value.
-    static char const *statusString(Status);
+    static const char* statusString(Status);
 
     // The name of this service, for logging.
     std::string const label_;
@@ -196,12 +252,18 @@
     // True if extensions can be granted to expired timers.
     const bool extend_;
 
+    // True if the service should freeze anr'ed processes.
+    const bool freeze_;
+
     // The global lock
     mutable Mutex lock_;
 
     // The list of all timers that are still running.  This is sorted by ID for fast lookup.
     std::set<Timer> running_;
 
+    // The list of all expired timers that are awaiting release.
+    std::set<Timer> expired_;
+
     // The maximum number of active timers.
     size_t maxRunning_;
 
@@ -214,6 +276,7 @@
         size_t discarded;
         size_t expired;
         size_t extended;
+        size_t released;
 
         // The number of times there were zero active timers.
         size_t drained;
@@ -281,6 +344,7 @@
     int const uid;
     nsecs_t const timeout;
     bool const extend;
+    bool const freeze;
 
     // The state of this timer.
     Status status;
@@ -294,6 +358,9 @@
     // True if this timer has been extended.
     bool extended;
 
+    // True if the process has been frozen.
+    bool frozen;
+
     // Bookkeeping for extensions.  The initial state of the process.  This is collected only if
     // the timer is extensible.
     ProcessStats initial;
@@ -306,10 +373,12 @@
             uid(0),
             timeout(0),
             extend(false),
+            freeze(false),
             status(Invalid),
             started(0),
             scheduled(0),
-            extended(false) {
+            extended(false),
+            frozen(false) {
     }
 
     // This constructor creates a timer with the specified id.  This can be used as the argument
@@ -320,40 +389,51 @@
             uid(0),
             timeout(0),
             extend(false),
+            freeze(false),
             status(Invalid),
             started(0),
             scheduled(0),
-            extended(false) {
+            extended(false),
+            frozen(false) {
     }
 
     // Create a new timer.  This starts the timer.
-    Timer(int pid, int uid, nsecs_t timeout, bool extend) :
+    Timer(int pid, int uid, nsecs_t timeout, bool extend, bool freeze) :
             id(nextId()),
             pid(pid),
             uid(uid),
             timeout(timeout),
             extend(extend),
+            freeze(pid != 0 && freeze),
             status(Running),
             started(now()),
             scheduled(started + timeout),
-            extended(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());
     }
 
-    // Cancel a timer.  Return the headroom (which may be negative).  This does not, as yet,
-    // account for extensions.
+    // Start a timer.  This interface exists to generate log messages, if enabled.
+    void start() {
+        event("start", /* verbose= */ true);
+    }
+
+    // Cancel a timer.
     void cancel() {
-        ALOGW_IF(DEBUG && status != Running, "cancel %s", toString().c_str());
+        ALOGW_IF(DEBUG_ERROR && status != Running, "error: canceling %s", toString().c_str());
         status = Canceled;
+        event("cancel");
     }
 
     // Expire a timer. Return true if the timer is expired and false otherwise.  The function
     // returns false if the timer is eligible for extension.  If the function returns false, the
     // scheduled time is updated.
     bool expire() {
-        ALOGI_IF(DEBUG, "expire %s", toString().c_str());
         nsecs_t extension = 0;
         if (extend && !extended) {
             // Only one extension is permitted.
@@ -366,18 +446,37 @@
         }
         if (extension == 0) {
             status = Expired;
+            maybeFreezeProcess();
+            event("expire");
         } else {
             scheduled += extension;
+            event("extend");
         }
         return status == Expired;
     }
 
-    // Accept a timeout.
+    // Accept a timeout.  This does nothing other than log the state machine change.
     void accept() {
+        event("accept");
     }
 
     // Discard a timeout.
     void discard() {
+        maybeUnfreezeProcess();
+        status = Canceled;
+        event("discard");
+    }
+
+    // Release the timer.
+    void release() {
+        // If timer represents a frozen process, unfreeze it at this time.
+        maybeUnfreezeProcess();
+        event("release");
+    }
+
+    // Return true if this timer corresponds to a running process.
+    bool alive() const {
+        return processExists(pid);
     }
 
     // Timers are sorted by id, which is unique.  This provides fast lookups.
@@ -390,13 +489,14 @@
     }
 
     std::string toString() const {
-        return StringPrintf("timer id=%d pid=%d status=%s", id, pid, statusString(status));
+        return StringPrintf("id=%d pid=%d uid=%d status=%s",
+                            id, pid, uid, statusString(status));
     }
 
     std::string toString(nsecs_t now) const {
         uint32_t ms = nanoseconds_to_milliseconds(now - scheduled);
-        return StringPrintf("timer id=%d pid=%d status=%s scheduled=%ums",
-                            id, pid, statusString(status), -ms);
+        return StringPrintf("id=%d pid=%d uid=%d status=%s scheduled=%ums",
+                            id, pid, uid, statusString(status), -ms);
     }
 
     static int maxId() {
@@ -404,6 +504,56 @@
     }
 
   private:
+    /**
+     * Collect the name of the process.
+     */
+    std::string getName() const {
+        return getProcessName(pid);
+    }
+
+    /**
+     * Freeze the process identified here.  Failures are not logged, as they are primarily due
+     * to a process having died (therefore failed to respond).
+     */
+    void maybeFreezeProcess() {
+        if (!freeze || !alive()) return;
+
+        // Construct a unique event ID.  The id*2 spans from the beginning of the freeze to the
+        // end of the freeze.  The id*2+1 spans the period inside the freeze/unfreeze
+        // operations.
+        const uint32_t cookie = id << 1;
+
+        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);
+        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);
+        } 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);
+        }
+    }
+
+    void maybeUnfreezeProcess() {
+        if (!freeze || !frozen) return;
+
+        // See maybeFreezeProcess for an explanation of the cookie.
+        const uint32_t cookie = id << 1;
+
+        ATRACE_ASYNC_FOR_TRACK_END(ANR_TIMER_TRACK, cookie+1);
+        if (SetProcessProfiles(uid, pid, {"Unfrozen"})) {
+            ALOGI("unfreeze %s name=%s", toString().c_str(), getName().c_str());
+            frozen = false;
+        } else {
+            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);
+    }
+
     // Get the next free ID.  NOTIMER is never returned.
     static timer_id_t nextId() {
         timer_id_t id = idGen.fetch_add(1);
@@ -413,6 +563,22 @@
         return id;
     }
 
+    // Log an event, non-verbose.
+    void event(char const* tag) {
+        event(tag, false);
+    }
+
+    // Log an event, guarded by the debug flag.
+    void event(char const* tag, bool verbose) {
+        if (verbose) {
+            char name[PATH_MAX];
+            ALOGI_IF(DEBUG_TIMER, "event %s %s name=%s",
+                     tag, toString().c_str(), getName().c_str());
+        } else {
+            ALOGI_IF(DEBUG_TIMER, "event %s id=%u", tag, id);
+        }
+    }
+
     // IDs start at 1.  A zero ID is invalid.
     static std::atomic<timer_id_t> idGen;
 };
@@ -494,6 +660,7 @@
         timer_id_t front = headTimerId();
         auto found = running_.find(key);
         if (found != running_.end()) running_.erase(found);
+        if (running_.empty()) drained_++;
     }
 
     // Remove every timer associated with the service.
@@ -536,7 +703,7 @@
     // A simple wrapper that meets the requirements of pthread_create.
     static void* run(void* arg) {
         reinterpret_cast<Ticker*>(arg)->monitor();
-        ALOGI("monitor exited");
+        ALOGI_IF(DEBUG_TICKER, "monitor exited");
         return 0;
     }
 
@@ -591,7 +758,7 @@
             };
             timer_settime(timerFd_, 0, &setting, nullptr);
             restarted_++;
-            ALOGI_IF(DEBUG, "restarted timerfd for %ld.%09ld", sec, ns);
+            ALOGI_IF(DEBUG_TICKER, "restarted timerfd for %ld.%09ld", sec, ns);
         } else {
             const struct itimerspec setting = {
                 .it_interval = { 0, 0 },
@@ -599,7 +766,7 @@
             };
             timer_settime(timerFd_, 0, &setting, nullptr);
             drained_++;
-            ALOGI_IF(DEBUG, "drained timer list");
+            ALOGI_IF(DEBUG_TICKER, "drained timer list");
         }
     }
 
@@ -641,19 +808,20 @@
 
 
 AnrTimerService::AnrTimerService(char const* label, notifier_t notifier, void* cookie,
-            jweak jtimer, Ticker* ticker, bool extend) :
+            jweak jtimer, Ticker* ticker, bool extend, bool freeze) :
         label_(label),
         notifier_(notifier),
         notifierCookie_(cookie),
         notifierObject_(jtimer),
         extend_(extend),
+        freeze_(freeze),
         ticker_(ticker) {
 
     // Zero the statistics
     maxRunning_ = 0;
     memset(&counters_, 0, sizeof(counters_));
 
-    ALOGI_IF(DEBUG, "initialized %s", label);
+    ALOGI_IF(DEBUG_TIMER, "initialized %s", label);
 }
 
 AnrTimerService::~AnrTimerService() {
@@ -661,7 +829,7 @@
     ticker_->remove(this);
 }
 
-char const *AnrTimerService::statusString(Status s) {
+const char* AnrTimerService::statusString(Status s) {
     switch (s) {
         case Invalid: return "invalid";
         case Running: return "running";
@@ -672,21 +840,18 @@
 }
 
 AnrTimerService::timer_id_t AnrTimerService::start(int pid, int uid, nsecs_t timeout) {
-    ALOGI_IF(DEBUG, "starting");
     AutoMutex _l(lock_);
-    Timer t(pid, uid, timeout, extend_);
-    insert(t);
+    Timer t(pid, uid, timeout, extend_, freeze_);
+    insertLocked(t);
+    t.start();
     counters_.started++;
-
-    ALOGI_IF(DEBUG, "started timer %u timeout=%zu", t.id, static_cast<size_t>(timeout));
     return t.id;
 }
 
 bool AnrTimerService::cancel(timer_id_t timerId) {
-    ALOGI_IF(DEBUG, "canceling %u", timerId);
     if (timerId == NOTIMER) return false;
     AutoMutex _l(lock_);
-    Timer timer = remove(timerId);
+    Timer timer = removeLocked(timerId);
 
     bool result = timer.status == Running;
     if (timer.status != Invalid) {
@@ -695,32 +860,32 @@
         counters_.error++;
     }
     counters_.canceled++;
-    ALOGI_IF(DEBUG, "canceled timer %u", timerId);
     return result;
 }
 
 bool AnrTimerService::accept(timer_id_t timerId) {
-    ALOGI_IF(DEBUG, "accepting %u", timerId);
     if (timerId == NOTIMER) return false;
     AutoMutex _l(lock_);
-    Timer timer = remove(timerId);
+    Timer timer = removeLocked(timerId);
 
-    bool result = timer.status == Expired;
+    bool result = false;
     if (timer.status == Expired) {
         timer.accept();
+        if (timer.frozen) {
+            addExpiredLocked(timer);
+            result = true;
+        }
     } else {
         counters_.error++;
     }
     counters_.accepted++;
-    ALOGI_IF(DEBUG, "accepted timer %u", timerId);
     return result;
 }
 
 bool AnrTimerService::discard(timer_id_t timerId) {
-    ALOGI_IF(DEBUG, "discarding %u", timerId);
     if (timerId == NOTIMER) return false;
     AutoMutex _l(lock_);
-    Timer timer = remove(timerId);
+    Timer timer = removeLocked(timerId);
 
     bool result = timer.status == Expired;
     if (timer.status == Expired) {
@@ -729,14 +894,48 @@
         counters_.error++;
     }
     counters_.discarded++;
-    ALOGI_IF(DEBUG, "discarded timer %u", timerId);
     return result;
 }
 
+bool AnrTimerService::release(timer_id_t id) {
+    if (id == NOTIMER) return true;
+
+    Timer key(id);
+    bool okay = false;
+    AutoMutex _l(lock_);
+    std::set<Timer>::iterator found = expired_.find(key);
+    if (found != expired_.end()) {
+        Timer t = *found;
+        t.release();
+        counters_.released++;
+        expired_.erase(found);
+        okay = true;
+    } else {
+        ALOGI_IF(DEBUG_ERROR, "error: unable to release (%u)", id);
+        counters_.error++;
+    }
+    scrubExpiredLocked();
+    return okay;
+}
+
+void AnrTimerService::addExpiredLocked(Timer const& timer) {
+    scrubExpiredLocked();
+    expired_.insert(timer);
+}
+
+void AnrTimerService::scrubExpiredLocked() {
+    for (auto i = expired_.begin(); i != expired_.end(); ) {
+        if (!i->alive()) {
+            i = expired_.erase(i);
+        } else {
+            i++;
+        }
+    }
+}
+
 // Hold the lock in order to manage the running list.
 // the listener.
 void AnrTimerService::expire(timer_id_t timerId) {
-    ALOGI_IF(DEBUG, "expiring %u", timerId);
     // Save the timer attributes for the notification
     int pid = 0;
     int uid = 0;
@@ -744,15 +943,15 @@
     bool expired = false;
     {
         AutoMutex _l(lock_);
-        Timer t = remove(timerId);
+        Timer t = removeLocked(timerId);
         expired = t.expire();
         if (t.status == Invalid) {
-            ALOGW_IF(DEBUG, "error: expired invalid timer %u", timerId);
+            ALOGW_IF(DEBUG_ERROR, "error: expired invalid timer %u", timerId);
             return;
         } else {
             // The timer is either Running (because it was extended) or expired (and is awaiting an
             // accept or discard).
-            insert(t);
+            insertLocked(t);
         }
         pid = t.pid;
         uid = t.uid;
@@ -771,13 +970,12 @@
             AutoMutex _l(lock_);
             // Notification failed, which means the listener will never call accept() or
             // discard().  Do not reinsert the timer.
-            remove(timerId);
+            discard(timerId);
         }
     }
-    ALOGI_IF(DEBUG, "expired timer %u", timerId);
 }
 
-void AnrTimerService::insert(const Timer& t) {
+void AnrTimerService::insertLocked(const Timer& t) {
     running_.insert(t);
     if (t.status == Running) {
         // Only forward running timers to the ticker.  Expired timers are handled separately.
@@ -786,7 +984,7 @@
     maxRunning_ = std::max(maxRunning_, running_.size());
 }
 
-AnrTimerService::Timer AnrTimerService::remove(timer_id_t timerId) {
+AnrTimerService::Timer AnrTimerService::removeLocked(timer_id_t timerId) {
     Timer key(timerId);
     auto found = running_.find(key);
     if (found != running_.end()) {
@@ -814,6 +1012,9 @@
                              counters_.error,
                              running_.size(),
                              maxRunning_));
+    r.push_back(StringPrintf("released:%zu releasing:%zu",
+                             counters_.released,
+                             expired_.size()));
     r.push_back(StringPrintf("ticker:%zu ticking:%zu maxTicking:%zu",
                              ticker_->id(),
                              ticker_->running(),
@@ -867,7 +1068,8 @@
     return nativeSupportEnabled;
 }
 
-jlong anrTimerCreate(JNIEnv* env, jobject jtimer, jstring jname, jboolean extend) {
+jlong anrTimerCreate(JNIEnv* env, jobject jtimer, jstring jname,
+                     jboolean extend, jboolean freeze) {
     if (!nativeSupportEnabled) return 0;
     AutoMutex _l(gAnrLock);
     if (gAnrArgs.ticker == nullptr) {
@@ -877,7 +1079,7 @@
     ScopedUtfChars name(env, jname);
     jobject timer = env->NewWeakGlobalRef(jtimer);
     AnrTimerService* service = new AnrTimerService(name.c_str(),
-            anrNotify, &gAnrArgs, timer, gAnrArgs.ticker, extend);
+            anrNotify, &gAnrArgs, timer, gAnrArgs.ticker, extend, freeze);
     return reinterpret_cast<jlong>(service);
 }
 
@@ -917,6 +1119,11 @@
     return toService(ptr)->discard(timerId);
 }
 
+jboolean anrTimerRelease(JNIEnv* env, jclass, jlong ptr, jint timerId) {
+    if (!nativeSupportEnabled) return false;
+    return toService(ptr)->release(timerId);
+}
+
 jobjectArray anrTimerDump(JNIEnv *env, jclass, jlong ptr) {
     if (!nativeSupportEnabled) return nullptr;
     std::vector<std::string> stats = toService(ptr)->getDump();
@@ -930,12 +1137,13 @@
 
 static const JNINativeMethod methods[] = {
     {"nativeAnrTimerSupported", "()Z",  (void*) anrTimerSupported},
-    {"nativeAnrTimerCreate",   "(Ljava/lang/String;Z)J", (void*) anrTimerCreate},
+    {"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},
 };
 
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index f47a59d..4be21d8 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -131,17 +131,28 @@
     }
 
     std::function<void()> createCallback(jlong vibrationId) {
-        return [vibrationId, this]() {
+        auto callbackId = ++mCallbackId;
+        return [vibrationId, callbackId, this]() {
+            auto currentCallbackId = mCallbackId.load();
+            if (currentCallbackId != callbackId) {
+                // This callback is from an older HAL call that is no longer relevant to the service
+                return;
+            }
             auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
             jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId,
                                    vibrationId);
         };
     }
 
+    void disableOldCallbacks() {
+        mCallbackId++;
+    }
+
 private:
     const std::shared_ptr<vibrator::HalController> mHal;
     const int32_t mVibratorId;
     const jobject mCallbackListener;
+    std::atomic<int64_t> mCallbackId;
 };
 
 static aidl::BrakingPwle brakingPwle(aidl::Braking braking, int32_t duration) {
@@ -236,6 +247,7 @@
     }
     auto offFn = [](vibrator::HalWrapper* hal) { return hal->off(); };
     wrapper->halCall<void>(offFn, "off");
+    wrapper->disableOldCallbacks();
 }
 
 static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong ptr, jfloat amplitude) {
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 610b502..eeb8b9b 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -779,6 +779,21 @@
     </xs:complexType>
 
     <xs:complexType name="blockingZoneConfig">
+        <!-- list of supported modes for blocking zone . Each point corresponds to one mode.
+            Supported only for lowerBlockingZoneConfigs
+            Mode format is : first = refreshRate, second = vsyncRate. E.g. :
+            <supportedModes>
+                <point>
+                    <first>60</first>   // refreshRate
+                    <second>60</second> //vsyncRate
+                </point>
+                ....
+            </supportedModes>
+        -->
+        <xs:element type="nonNegativeFloatToFloatMap" name="supportedModes" minOccurs="0">
+            <xs:annotation name="nullable"/>
+            <xs:annotation name="final"/>
+        </xs:element>
         <xs:element name="defaultRefreshRate" type="xs:nonNegativeInteger"
                     minOccurs="1" maxOccurs="1">
             <xs:annotation name="final"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 203a6d9..757b23a 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -35,9 +35,11 @@
     method public final com.android.server.display.config.BlockingZoneThreshold getBlockingZoneThreshold();
     method public final java.math.BigInteger getDefaultRefreshRate();
     method @Nullable public final String getRefreshRateThermalThrottlingId();
+    method @Nullable public final com.android.server.display.config.NonNegativeFloatToFloatMap getSupportedModes();
     method public final void setBlockingZoneThreshold(com.android.server.display.config.BlockingZoneThreshold);
     method public final void setDefaultRefreshRate(java.math.BigInteger);
     method public final void setRefreshRateThermalThrottlingId(@Nullable String);
+    method public final void setSupportedModes(@Nullable com.android.server.display.config.NonNegativeFloatToFloatMap);
   }
 
   public class BlockingZoneThreshold {
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index da965bb..32b571a 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -17,8 +17,10 @@
 java_library_static {
     name: "services.devicepolicy",
     defaults: ["platform_service_defaults"],
-    srcs: [":services.devicepolicy-sources"],
-
+    srcs: [
+        ":services.devicepolicy-sources",
+        ":statslog-devicepolicy-java-gen",
+    ],
     libs: [
         "services.core",
         "app-compat-annotations",
@@ -27,3 +29,11 @@
         "androidx.annotation_annotation",
     ],
 }
+
+genrule {
+    name: "statslog-devicepolicy-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module devicepolicy" +
+        " --javaPackage com.android.server.devicepolicy --javaClass DevicePolicyStatsLog",
+    out: ["com/android/server/devicepolicy/DevicePolicyStatsLog.java"],
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bdd0730..e122fe0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -109,6 +109,7 @@
 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_SUSPENSION;
 import static android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
 import static android.app.AppOpsManager.OP_RUN_IN_BACKGROUND;
+import static android.app.StatsManager.PULL_SUCCESS;
 import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
 import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
 import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
@@ -265,12 +266,26 @@
 import static android.security.keystore.AttestationUtils.USE_INDIVIDUAL_ATTESTATION;
 
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
 import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
 import static com.android.server.devicepolicy.DevicePolicyEngine.DEFAULT_POLICY_SIZE_LIMIT;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__COPE;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__DEVICE_OWNER;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__DEVICE_OWNER_FINANCED;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__MANAGEMENT_MODE_UNSPECIFIED;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__PROFILE_OWNER;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_HIGH;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_LEGACY;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_LOW;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_MEDIUM;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_NONE;
+import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_UNSPECIFIED;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -305,6 +320,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.StatsManager;
 import android.app.StatusBarManager;
 import android.app.admin.AccountTypePolicyKey;
 import android.app.admin.BooleanPolicyValue;
@@ -477,6 +493,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsEvent;
 import android.util.Xml;
 import android.view.IWindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -804,11 +821,11 @@
     public static final long EXPLICIT_WIPE_BEHAVIOUR = 242193913L;
 
     /**
-     * Apps targetting U+ should now expect that attempts to grant sensor permissions without
+     * Apps targeting V+ should now expect that attempts to grant sensor permissions without
      * authorisation will result in a security exception.
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
     public static final long THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS = 277035314L;
 
     /**
@@ -2256,41 +2273,11 @@
                 if (userHandle == UserHandle.USER_SYSTEM) {
                     mStateCache.setDeviceProvisioned(policy.mUserSetupComplete);
                 }
-                if (Flags.headlessSingleUserBadDeviceAdminStateFix()) {
-                    fixBadDeviceAdminStateForInternalUsers(userHandle, policy);
-                }
             }
             return policy;
         }
     }
 
-    private void fixBadDeviceAdminStateForInternalUsers(int userId, DevicePolicyData policy) {
-        ComponentName component = mOwners.getDeviceOwnerComponent();
-        int doUserId = mOwners.getDeviceOwnerUserId();
-        ComponentName cloudDpc = new ComponentName(
-                "com.google.android.apps.work.clouddpc",
-                "com.google.android.apps.work.clouddpc.receivers.CloudDeviceAdminReceiver");
-        if (component == null || doUserId != userId || !component.equals(cloudDpc)) {
-            return;
-        }
-        Slogf.i(LOG_TAG, "Attempting to apply a temp fix for cloudpc internal users' bad state.");
-        final int n = policy.mAdminList.size();
-        for (int i = 0; i < n; i++) {
-            ActiveAdmin admin = policy.mAdminList.get(i);
-            if (component.equals(admin.info.getComponent())) {
-                Slogf.i(LOG_TAG, "An ActiveAdmin already exists, fix not required.");
-                return;
-            }
-        }
-        DeviceAdminInfo dai = findAdmin(component, userId, /* throwForMissingPermission= */ false);
-        if (dai != null) {
-            ActiveAdmin ap = new ActiveAdmin(dai, /* parent */ false);
-            policy.mAdminMap.put(ap.info.getComponent(), ap);
-            policy.mAdminList.add(ap);
-            Slogf.i(LOG_TAG, "Fix applied, an ActiveAdmin has been added.");
-        }
-    }
-
     /**
      * Creates and loads the policy data from xml for data that is shared between
      * various profiles of a user. In contrast to {@link #getUserData(int)}
@@ -3378,6 +3365,9 @@
                 synchronized (getLockObject()) {
                     mDevicePolicyEngine.reapplyAllPoliciesOnBootLocked();
                 }
+                if (Flags.managementModePolicyMetrics()) {
+                    registerStatsCallbacks();
+                }
                 break;
             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
                 synchronized (getLockObject()) {
@@ -3517,6 +3507,121 @@
         return true;
     }
 
+    /** Register callbacks for statsd pulled atoms. */
+    private void registerStatsCallbacks() {
+        final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+        if (statsManager == null) {
+            Slog.wtf(LOG_TAG, "StatsManager system service not found.");
+            return;
+        }
+        statsManager.setPullAtomCallback(
+                DEVICE_POLICY_MANAGEMENT_MODE,
+                null, // use defaultPullAtomMetadata values
+                DIRECT_EXECUTOR,
+                this::onPullManagementModeAtom);
+        statsManager.setPullAtomCallback(
+                DEVICE_POLICY_STATE,
+                null, // use defaultPullAtomMetadata values
+                DIRECT_EXECUTOR,
+                this::onPullPolicyStateAtom);
+    }
+
+    /** Writes the pulled atoms. */
+    private int onPullManagementModeAtom(int atomTag, List<StatsEvent> statsEvents) {
+        synchronized (getLockObject()) {
+            statsEvents.add(DevicePolicyStatsLog.buildStatsEvent(
+                    DEVICE_POLICY_MANAGEMENT_MODE,
+                    getStatsManagementModeLocked().managementMode()));
+            return PULL_SUCCESS;
+        }
+    }
+
+    /** Writes the pulled atoms. */
+    private int onPullPolicyStateAtom(int atomTag, List<StatsEvent> statsEvents) {
+        synchronized (getLockObject()) {
+            StatsManagementMode statsManagementMode = getStatsManagementModeLocked();
+            if (statsManagementMode.admin() != null) {
+                statsEvents.add(DevicePolicyStatsLog.buildStatsEvent(DEVICE_POLICY_STATE,
+                        getRequiredPasswordComplexityStatsLocked(statsManagementMode.admin()),
+                        statsManagementMode.managementMode()
+                        ));
+            } else {
+                statsEvents.add(DevicePolicyStatsLog.buildStatsEvent(DEVICE_POLICY_STATE,
+                        DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_NONE,
+                        DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__MANAGEMENT_MODE_UNSPECIFIED
+                ));
+            }
+            return PULL_SUCCESS;
+        }
+    }
+
+    private StatsManagementMode getStatsManagementModeLocked() {
+        int managementMode =
+                DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__MANAGEMENT_MODE_UNSPECIFIED;
+        ActiveAdmin admin = getDeviceOwnerAdminLocked();
+        if (admin != null) {
+            managementMode = getDeviceOwnerTypeLocked(
+                    getDeviceOwnerComponent(false).getPackageName())
+                    != DEVICE_OWNER_TYPE_FINANCED
+                    ? DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__DEVICE_OWNER
+                    : DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__DEVICE_OWNER_FINANCED;
+        } else {
+            // Find the first user with managing_app.
+            for (Integer profileUserId : mOwners.getProfileOwnerKeys()) {
+                if (isManagedProfile(profileUserId)) {
+                    admin = getProfileOwnerAdminLocked(profileUserId);
+                    managementMode = mOwners.isProfileOwnerOfOrganizationOwnedDevice(
+                            profileUserId)
+                            ? DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__COPE
+                            : DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__PROFILE_OWNER;
+                    break;
+                }
+            }
+        }
+        return new StatsManagementMode(managementMode, admin);
+    }
+
+    private record StatsManagementMode(int managementMode, ActiveAdmin admin) {
+    }
+
+    @GuardedBy("getLockObject()")
+    private int getRequiredPasswordComplexityStatsLocked(ActiveAdmin admin) {
+        int userId = admin.getUserHandle().getIdentifier();
+        EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                admin.info.getComponent(),
+                userId,
+                admin);
+
+        Integer passwordComplexity = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+                PolicyDefinition.PASSWORD_COMPLEXITY,
+                enforcingAdmin,
+                userId);
+        if (passwordComplexity == null) {
+            return admin.mPasswordPolicy.quality != PASSWORD_QUALITY_UNSPECIFIED
+                    ? DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_LEGACY
+                    : DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_UNSPECIFIED;
+        }
+        switch (passwordComplexity) {
+            case PASSWORD_COMPLEXITY_NONE -> {
+                return DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_NONE;
+            }
+            case PASSWORD_COMPLEXITY_LOW -> {
+                return DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_LOW;
+            }
+            case PASSWORD_COMPLEXITY_MEDIUM -> {
+                return DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_MEDIUM;
+            }
+            case PASSWORD_COMPLEXITY_HIGH -> {
+                return DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_HIGH;
+            }
+            default -> {
+                Slogf.wtf(LOG_TAG, "Unhandled password complexity: " + passwordComplexity);
+                // The following line is unreachable as Slogf.wtf crashes the process.
+                // But we need this to avoid compilation error missing return statement.
+                return DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_UNSPECIFIED;
+            }
+        }
+    }
 
     private void applyManagedSubscriptionsPolicyIfRequired() {
         int copeProfileUserId = getOrganizationOwnedProfileUserId();
@@ -4145,6 +4250,10 @@
     private void clearOrgOwnedProfileOwnerUserRestrictions(UserHandle parentUserHandle) {
         mUserManager.setUserRestriction(
                 UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false, parentUserHandle);
+        if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                    false, UserHandle.SYSTEM);
+        }
         mUserManager.setUserRestriction(
                 UserManager.DISALLOW_ADD_USER, false, parentUserHandle);
     }
@@ -16766,7 +16875,7 @@
                     caller.getUserId());
             if (SENSOR_PERMISSIONS.contains(permission)
                     && grantState == PERMISSION_GRANT_STATE_GRANTED
-                    && (!canAdminGrantSensorsPermissions() || isCallerDelegate(caller))) {
+                    && !canAdminGrantSensorsPermissions()) {
                 if (mInjector.isChangeEnabled(THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS,
                         caller.getPackageName(), caller.getUserId())) {
                     throw new SecurityException(
@@ -16789,6 +16898,20 @@
                     || isFinancedDeviceOwner(caller)))
                     || (caller.hasPackage() && isCallerDelegate(caller,
                     DELEGATION_PERMISSION_GRANT)));
+            if (SENSOR_PERMISSIONS.contains(permission)
+                    && grantState == PERMISSION_GRANT_STATE_GRANTED
+                    && !canAdminGrantSensorsPermissions()) {
+                if (mInjector.isChangeEnabled(THROW_SECURITY_EXCEPTION_FOR_SENSOR_PERMISSIONS,
+                        caller.getPackageName(), caller.getUserId())) {
+                    throw new SecurityException(
+                            "Caller not permitted to grant sensor permissions.");
+                } else {
+                    Slogf.e(LOG_TAG, "Caller attempted to grant sensor permissions but denied");
+                    // This is to match the legacy behaviour.
+                    callback.sendResult(Bundle.EMPTY);
+                    return;
+                }
+            }
             synchronized (getLockObject()) {
                 long ident = mInjector.binderClearCallingIdentity();
                 try {
@@ -17890,6 +18013,12 @@
             mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
                     isProfileOwnerOnOrganizationOwnedDevice,
                     parentUser);
+            if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+                // For HSUM, additionally set this on user 0 to block ADB from removing the profile.
+                mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                        isProfileOwnerOnOrganizationOwnedDevice,
+                        UserHandle.SYSTEM);
+            }
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER,
                     isProfileOwnerOnOrganizationOwnedDevice,
                     parentUser);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index a0ea4e9..84d2b7f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -240,7 +240,7 @@
                     POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE
                             | POLICY_FLAG_NON_COEXISTABLE_POLICY
                             | POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED,
-                    PolicyEnforcerCallbacks::noOp,
+                    PolicyEnforcerCallbacks::setApplicationRestrictions,
                     new BundlePolicySerializer());
 
     /**
@@ -545,7 +545,7 @@
             USER_RESTRICTION_FLAGS.put(
                     UserManager.DISALLOW_THREAD_NETWORK, POLICY_FLAG_GLOBAL_ONLY_POLICY);
         }
-
+        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ASSIST_CONTENT, /* flags= */ 0);
         for (String key : USER_RESTRICTION_FLAGS.keySet()) {
             createAndAddUserRestrictionPolicyDefinition(key, USER_RESTRICTION_FLAGS.get(key));
         }
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 957d7c3..ce05c82 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -66,7 +66,6 @@
         "libprotobuf-cpp-lite",
         "service.incremental.proto",
         "libvold_binder",
-        "libc++fs",
         "libziparchive_for_incfs",
     ],
     shared_libs: [
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cfe4e17..611a4eb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1196,11 +1196,12 @@
         mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class);
         t.traceEnd();
 
+        // Initialize RescueParty.
+        RescueParty.registerHealthObserver(mSystemContext);
         if (!Flags.recoverabilityDetection()) {
             // Now that we have the bare essentials of the OS up and running, take
             // note that we just booted, which might send out a rescue party if
             // we're stuck in a runtime restart loop.
-            RescueParty.registerHealthObserver(mSystemContext);
             PackageWatchdog.getInstance(mSystemContext).noteBoot();
         }
 
@@ -1923,7 +1924,11 @@
             startRotationResolverService(context, t);
             startSystemCaptionsManagerService(context, t);
             startTextToSpeechManagerService(context, t);
-            startWearableSensingService(t);
+            if (!isWatch || !android.server.Flags.removeWearableSensingServiceFromWear()) {
+                startWearableSensingService(t);
+            } else {
+                Slog.d(TAG, "Not starting WearableSensingService");
+            }
             startOnDeviceIntelligenceService(t);
 
             if (deviceHasConfigString(
@@ -2917,10 +2922,10 @@
         t.traceEnd();
 
         if (Flags.recoverabilityDetection()) {
-            // Now that we have the essential services needed for rescue party, initialize
-            // RescuParty. note that we just booted, which might send out a rescue party if
-            // we're stuck in a runtime restart loop.
-            RescueParty.registerHealthObserver(mSystemContext);
+            // Now that we have the essential services needed for mitigations, register the boot
+            // with package watchdog.
+            // Note that we just booted, which might send out a rescue party if we're stuck in a
+            // runtime restart loop.
             PackageWatchdog.getInstance(mSystemContext).noteBoot();
         }
 
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 38354e8..e8aa68c 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -14,4 +14,11 @@
      namespace: "wear_frameworks"
      description: "Remove TextServiceManagerService on Wear"
      bug: "323720705"
+}
+
+flag {
+     name: "remove_wearable_sensing_service_from_wear"
+     namespace: "wear_frameworks"
+     description: "Remove WearableSensingManagerService on Wear"
+     bug: "340929916"
 }
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index 3bdcd9b..161a816 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -246,24 +246,32 @@
         }
 
     override fun setUidMode(uid: Int, deviceId: String, code: Int, mode: Int): Boolean {
-        if (
-            Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames
-        ) {
-            Slog.w(
-                LOG_TAG,
-                "Cannot set UID mode for runtime permission app op, " +
-                    " callingUid = ${Binder.getCallingUid()}, " +
-                    " uid = $uid," +
-                    " code = ${AppOpsManager.opToName(code)}," +
-                    " mode = ${AppOpsManager.modeToName(mode)}",
-                RuntimeException()
-            )
-            return false
-        }
-
         val appId = UserHandle.getAppId(uid)
         val userId = UserHandle.getUserId(uid)
         val appOpName = AppOpsManager.opToPublicName(code)
+
+        if (
+            Flags.runtimePermissionAppopsMappingEnabled() && code in runtimeAppOpToPermissionNames
+        ) {
+            val oldMode =
+                service.getState { with(appIdPolicy) { getAppOpMode(appId, userId, appOpName) } }
+            val wouldHaveChanged = oldMode != mode
+            val logMessage =
+                (if (wouldHaveChanged) "Blocked" else "Ignored") +
+                    " setUidMode call for runtime permission app op:" +
+                    " uid = $uid," +
+                    " code = ${AppOpsManager.opToName(code)}," +
+                    " mode = ${AppOpsManager.modeToName(mode)}," +
+                    " callingUid = ${Binder.getCallingUid()}," +
+                    " oldMode = ${AppOpsManager.modeToName(oldMode)}"
+            if (wouldHaveChanged) {
+                Slog.e(LOG_TAG, logMessage, RuntimeException())
+            } else {
+                Slog.w(LOG_TAG, logMessage)
+            }
+            return false
+        }
+
         var wasChanged: Boolean
         service.mutateState {
             wasChanged = with(appIdPolicy) { setAppOpMode(appId, userId, appOpName, mode) }
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index d307200..bb0838d 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1277,10 +1277,11 @@
                     permissionName
                 )
             else ->
-                permissionAllowlist.getProductSignatureAppAllowlistState(
-                    packageName,
-                    permissionName
-                )
+                permissionAllowlist.getApexSignatureAppAllowlistState(packageName, permissionName)
+                    ?: permissionAllowlist.getProductSignatureAppAllowlistState(
+                        packageName,
+                        permissionName
+                    )
                     ?: permissionAllowlist.getVendorSignatureAppAllowlistState(
                         packageName,
                         permissionName
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 6499556..78dbc60 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -533,8 +533,7 @@
         val packageState =
             packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use {
                 it.getPackageState(packageName)
-            }
-                ?: return PackageManager.PERMISSION_DENIED
+            } ?: return PackageManager.PERMISSION_DENIED
 
         val isPermissionGranted =
             service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) }
@@ -1164,8 +1163,7 @@
         val packageState =
             packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use {
                 it.getPackageState(packageName)
-            }
-                ?: return false
+            } ?: return false
 
         service.getState {
             if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
@@ -1216,8 +1214,7 @@
         val packageState =
             packageManagerLocal.withFilteredSnapshot(callingUid, userId).use {
                 it.getPackageState(packageName)
-            }
-                ?: return false
+            } ?: return false
         val appId = packageState.appId
         if (UserHandle.getAppId(callingUid) != appId) {
             return false
@@ -1546,8 +1543,7 @@
         val packageState =
             packageManagerLocal.withFilteredSnapshot(callingUid, userId).use {
                 it.getPackageState(packageName)
-            }
-                ?: return null
+            } ?: return null
         val androidPackage = packageState.androidPackage ?: return null
 
         val isCallerPrivileged =
@@ -1710,8 +1706,7 @@
                     PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER,
                     userId
                 )
-                ?.let { ArraySet(permissionNames).apply { this += it }.toList() }
-                ?: permissionNames
+                ?.let { ArraySet(permissionNames).apply { this += it }.toList() } ?: permissionNames
 
         setAllowlistedRestrictedPermissionsUnchecked(
             androidPackage,
@@ -2753,27 +2748,25 @@
             ) {
                 return false
             }
-            return try {
-                val contentResolver = context.contentResolver
-                val userId = UserHandle.getUserId(uid)
-                val isInSetup =
-                    Settings.Secure.getIntForUser(
-                        contentResolver,
-                        Settings.Secure.USER_SETUP_COMPLETE,
-                        userId
-                    ) == 0
-                val isInDeferredSetup =
-                    Settings.Secure.getIntForUser(
-                        contentResolver,
-                        Settings.Secure.USER_SETUP_PERSONALIZATION_STATE,
-                        userId
-                    ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
-                isInSetup || isInDeferredSetup
-            } catch (e: Settings.SettingNotFoundException) {
-                Slog.w(LOG_TAG, "Failed to check if the user is in restore: $e")
-                false
-            }
+
+            val userId = UserHandle.getUserId(uid)
+
+            val isInSetup = getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId) == 0
+            if (isInSetup) return true
+
+            val isInDeferredSetup =
+                getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) ==
+                    Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED
+            return isInDeferredSetup
         }
+
+        private fun getSecureInt(settingName: String, userId: Int): Int? =
+            try {
+                Settings.Secure.getIntForUser(context.contentResolver, settingName, userId)
+            } catch (e: Settings.SettingNotFoundException) {
+                Slog.i(LOG_TAG, "Setting $settingName not found", e)
+                null
+            }
     }
 
     private class OnPermissionsChangeListeners(looper: Looper) : Handler(looper) {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 33bf4bd..cd70ed2 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.hardware.camera2.CameraManager;
 import android.os.Handler;
 import android.os.IBinder.DeathRecipient;
 import android.os.Looper;
@@ -258,6 +259,7 @@
         BackgroundThread.get().getThreadHandler().post(
                 () -> {
                     registerAppLaunchObserver();
+                    registerCameraOpenObserver();
                     registerDex2oatObserver();
                     registerOTAObserver();
                 });
@@ -321,16 +323,14 @@
                 "dex2oat_trace_freq", 25);
         int randomNum = ThreadLocalRandom.current().nextInt(100);
         if (randomNum < traceFrequency) {
-            BackgroundThread.get().getThreadHandler().post(() -> {
+            // Dex2oat could take a while before it starts. Add a short delay before start tracing.
+            BackgroundThread.get().getThreadHandler().postDelayed(() -> {
                 try {
-                    // Dex2oat could take a while before it starts. Add a short delay before start
-                    // tracing.
-                    Thread.sleep(1000);
                     mIProfcollect.trace_once("dex2oat");
-                } catch (RemoteException | InterruptedException e) {
+                } catch (RemoteException e) {
                     Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
                 }
-            });
+            }, 1000);
         }
     }
 
@@ -371,4 +371,36 @@
             pfs.getContext().sendBroadcast(intent);
         });
     }
+
+    private void registerCameraOpenObserver() {
+        CameraManager cm = getContext().getSystemService(CameraManager.class);
+        cm.registerAvailabilityCallback(new CameraManager.AvailabilityCallback() {
+            @Override
+            public void onCameraOpened(String cameraId, String packageId) {
+                Log.d(LOG_TAG, "Received camera open event from: " + packageId);
+                // Skip face auth and Android System Intelligence, since they trigger way too
+                // often.
+                if (packageId.startsWith("client.pid")
+                        || packageId.equals("com.google.android.as")) {
+                    return;
+                }
+                // Sample for a fraction of camera events.
+                final int traceFrequency =
+                        DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
+                        "camera_trace_freq", 10);
+                int randomNum = ThreadLocalRandom.current().nextInt(100);
+                if (randomNum >= traceFrequency) {
+                    return;
+                }
+                // Wait for 1s before starting tracing.
+                BackgroundThread.get().getThreadHandler().postDelayed(() -> {
+                    try {
+                        mIProfcollect.trace_once("camera");
+                    } catch (RemoteException e) {
+                        Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
+                    }
+                }, 1000);
+            }
+        }, null);
+    }
 }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
index 820628c..8e6954b 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidTest.xml
@@ -25,6 +25,12 @@
         <option name="test-file-name" value="FrameworksImeTests.apk" />
     </target_preparer>
 
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+        <option name="run-command" value="wm dismiss-keyguard" />
+        <option name="run-command" value="settings put secure immersive_mode_confirmations confirmed" />
+    </target_preparer>
+
     <option name="test-tag" value="FrameworksImeTests" />
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 1535298..2029b71 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -48,6 +48,7 @@
 
 import com.android.apps.inputmethod.simpleime.ims.InputMethodServiceWrapper;
 import com.android.apps.inputmethod.simpleime.testing.TestActivity;
+import com.android.compatibility.common.util.SystemUtil;
 import com.android.internal.inputmethod.InputMethodNavButtonFlags;
 
 import org.junit.After;
@@ -834,8 +835,7 @@
 
     private String executeShellCommand(String cmd) throws IOException {
         Log.i(TAG, "Run command: " + cmd);
-        return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
-                .executeShellCommand(cmd);
+        return SystemUtil.runShellCommandOrThrow(cmd);
     }
 
     private void clickOnEditorText() {
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 221a991..a4ca317 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -68,12 +68,15 @@
 public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase {
     private DefaultImeVisibilityApplier mVisibilityApplier;
 
+    private int mUserId = 0;
+
     @Before
     public void setUp() throws RemoteException {
         super.setUp();
         mVisibilityApplier =
                 (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
         synchronized (ImfLock.class) {
+            mUserId = mInputMethodManagerService.getCurrentImeUserIdLocked();
             mInputMethodManagerService.setAttachedClientForTesting(requireNonNull(
                     mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient)));
         }
@@ -103,7 +106,7 @@
         assertThrows(IllegalArgumentException.class, () -> {
             synchronized (ImfLock.class) {
                 mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
-                        STATE_INVALID);
+                        STATE_INVALID, mUserId);
             }
         });
     }
@@ -112,7 +115,8 @@
     public void testApplyImeVisibility_showIme() {
         final var statsToken = ImeTracker.Token.empty();
         synchronized (ImfLock.class) {
-            mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME);
+            mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_SHOW_IME,
+                    mUserId);
         }
         verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), eq(statsToken));
     }
@@ -121,7 +125,8 @@
     public void testApplyImeVisibility_hideIme() {
         final var statsToken = ImeTracker.Token.empty();
         synchronized (ImfLock.class) {
-            mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
+            mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME,
+                    mUserId);
         }
         verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt() /* displayId */,
                 eq(statsToken));
@@ -132,7 +137,7 @@
         mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
         synchronized (ImfLock.class) {
             mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
-                    STATE_HIDE_IME_EXPLICIT);
+                    STATE_HIDE_IME_EXPLICIT, mUserId);
         }
         verifyHideSoftInput(true, true);
     }
@@ -142,7 +147,7 @@
         mInputMethodManagerService.mImeWindowVis = IME_ACTIVE;
         synchronized (ImfLock.class) {
             mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
-                    STATE_HIDE_IME_NOT_ALWAYS);
+                    STATE_HIDE_IME_NOT_ALWAYS, mUserId);
         }
         verifyHideSoftInput(true, true);
     }
@@ -151,7 +156,7 @@
     public void testApplyImeVisibility_showImeImplicit() throws Exception {
         synchronized (ImfLock.class) {
             mVisibilityApplier.applyImeVisibility(mWindowToken, ImeTracker.Token.empty(),
-                    STATE_SHOW_IME_IMPLICIT);
+                    STATE_SHOW_IME_IMPLICIT, mUserId);
         }
         verifyShowSoftInput(true, true, 0 /* showFlags */);
     }
@@ -166,10 +171,13 @@
 
         final var statsToken = ImeTracker.Token.empty();
         synchronized (ImfLock.class) {
-            final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+            final var bindingController =
+                    mInputMethodManagerService.getInputMethodBindingController(mUserId);
+            final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
             // Verify hideIme will apply the expected displayId when the default IME
             // visibility applier app STATE_HIDE_IME.
-            mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME);
+            mVisibilityApplier.applyImeVisibility(mWindowToken, statsToken, STATE_HIDE_IME,
+                    mUserId);
             verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
                     eq(mWindowToken), eq(displayIdToShowIme), eq(statsToken));
         }
@@ -204,7 +212,9 @@
             // Simulate the system hides the IME when switching IME services in different users.
             // (e.g. unbinding the IME from the current user to the profile user)
             final var statsToken = ImeTracker.Token.empty();
-            final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+            final var bindingController =
+                    mInputMethodManagerService.getInputMethodBindingController(mUserId);
+            final int displayIdToShowIme = bindingController.getDisplayIdToShowIme();
             mInputMethodManagerService.hideCurrentInputLocked(mWindowToken,
                     statsToken, 0 /* flags */, null /* resultReceiver */,
                     HIDE_SWITCH_USER);
@@ -214,7 +224,7 @@
             // the IME hidden state.
             // The unbind will cancel the previous stats token, and create a new one internally.
             verify(mVisibilityApplier).applyImeVisibility(
-                    eq(mWindowToken), any(), eq(STATE_HIDE_IME));
+                    eq(mWindowToken), any(), eq(STATE_HIDE_IME), eq(mUserId) /* userId */);
             verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
                     eq(mWindowToken), eq(displayIdToShowIme), and(not(eq(statsToken)), notNull()));
         }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index 3b25cb1..42bd75a 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -127,7 +127,7 @@
     protected IInputMethodInvoker mMockInputMethodInvoker;
     protected InputMethodManagerService mInputMethodManagerService;
     protected ServiceThread mServiceThread;
-    protected ServiceThread mPackageMonitorThread;
+    protected ServiceThread mIoThread;
     protected boolean mIsLargeScreen;
     private InputManagerGlobal.TestSession mInputManagerGlobalSession;
 
@@ -226,14 +226,14 @@
                         "immstest1",
                         Process.THREAD_PRIORITY_FOREGROUND,
                         true /* allowIo */);
-        mPackageMonitorThread =
+        mIoThread =
                 new ServiceThread(
                         "immstest2",
                         Process.THREAD_PRIORITY_FOREGROUND,
                         true /* allowIo */);
         mInputMethodManagerService = new InputMethodManagerService(mContext,
                 InputMethodManagerService.shouldEnableExperimentalConcurrentMultiUserMode(mContext),
-                mServiceThread, mPackageMonitorThread,
+                mServiceThread, mIoThread,
                 unusedUserId -> mMockInputMethodBindingController);
         spyOn(mInputMethodManagerService);
 
@@ -267,8 +267,8 @@
             mInputMethodManagerService.mInputMethodDeviceConfigs.destroy();
         }
 
-        if (mPackageMonitorThread != null) {
-            mPackageMonitorThread.quitSafely();
+        if (mIoThread != null) {
+            mIoThread.quitSafely();
         }
 
         if (mServiceThread != null) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index d3efcb6..2a458c42 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -52,7 +52,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.display.brightness.clamper.BrightnessClamperController;
 import com.android.server.display.config.HysteresisLevels;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.testutils.OffsettableClock;
@@ -102,8 +101,6 @@
     @Mock Handler mNoOpHandler;
     @Mock BrightnessRangeController mBrightnessRangeController;
     @Mock
-    BrightnessClamperController mBrightnessClamperController;
-    @Mock
     DisplayManagerFlags mDisplayManagerFlags;
     @Mock BrightnessThrottler mBrightnessThrottler;
 
@@ -181,7 +178,7 @@
                 mContext, mBrightnessRangeController, mBrightnessThrottler,
                 useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
                 useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits,
-                mBrightnessClamperController, mDisplayManagerFlags
+                mDisplayManagerFlags
         );
 
         when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn(
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 46d08b0..9a25b1a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -948,6 +948,22 @@
         assertThat(supportedModeData.vsyncRate).isEqualTo(120);
     }
 
+    @Test
+    public void testLowLightBlockingZoneSupportedModesFromConfigFile() throws IOException {
+        setupDisplayDeviceConfigFromDisplayConfigFile();
+
+        RefreshRateData refreshRateData = mDisplayDeviceConfig.getRefreshRateData();
+        assertNotNull(refreshRateData);
+        assertThat(refreshRateData.lowLightBlockingZoneSupportedModes).hasSize(2);
+        SupportedModeData supportedModeData =
+                refreshRateData.lowLightBlockingZoneSupportedModes.get(0);
+        assertThat(supportedModeData.refreshRate).isEqualTo(60);
+        assertThat(supportedModeData.vsyncRate).isEqualTo(60);
+        supportedModeData = refreshRateData.lowLightBlockingZoneSupportedModes.get(1);
+        assertThat(supportedModeData.refreshRate).isEqualTo(240);
+        assertThat(supportedModeData.vsyncRate).isEqualTo(240);
+    }
+
     private String getValidLuxThrottling() {
         return "<luxThrottling>\n"
                 + "    <brightnessLimitMap>\n"
@@ -1117,6 +1133,19 @@
                 + "</lowPowerSupportedModes>\n";
     }
 
+    private String getLowLightVrrSupportedModesConfig() {
+        return "<supportedModes>\n"
+                + "    <point>\n"
+                + "        <first>60</first>\n"
+                + "        <second>60</second>\n"
+                + "    </point>\n"
+                + "    <point>\n"
+                + "        <first>240</first>\n"
+                + "        <second>240</second>\n"
+                + "    </point>\n"
+                + "</supportedModes>\n";
+    }
+
     private String getHdrBrightnessConfig() {
         return "<hdrBrightnessConfig>\n"
               + "    <brightnessMap>\n"
@@ -1624,6 +1653,7 @@
                 +                   "<nits>-1</nits>\n"
                 +               "</displayBrightnessPoint>\n"
                 +           "</blockingZoneThreshold>\n"
+                + getLowLightVrrSupportedModesConfig()
                 +       "</lowerBlockingZoneConfigs>\n"
                 +       "<higherBlockingZoneConfigs>\n"
                 +           "<defaultRefreshRate>90</defaultRefreshRate>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java
new file mode 100644
index 0000000..bacbf89
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceInfoTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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 static com.android.server.display.DisplayDeviceInfo.DIFF_COLOR_MODE;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_COMMITTED_STATE;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_HDR_SDR_RATIO;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_MODE_ID;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_RENDER_TIMINGS;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_ROTATION;
+import static com.android.server.display.DisplayDeviceInfo.DIFF_STATE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.Display;
+import android.view.Surface;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DisplayDeviceInfoTest {
+
+    @Test
+    public void testDiff_noChange() {
+        var oldDdi = createInfo();
+        var newDdi = createInfo();
+
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(0);
+    }
+
+    @Test
+    public void testDiff_state() {
+        var oldDdi = createInfo();
+        var newDdi = createInfo();
+
+        newDdi.state = Display.STATE_VR;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_STATE);
+    }
+
+    @Test
+    public void testDiff_committedState() {
+        var oldDdi = createInfo();
+        var newDdi = createInfo();
+
+        newDdi.committedState = Display.STATE_UNKNOWN;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COMMITTED_STATE);
+    }
+
+    @Test
+    public void testDiff_colorMode() {
+        var oldDdi = createInfo();
+        var newDdi = createInfo();
+
+        newDdi.colorMode = Display.COLOR_MODE_DISPLAY_P3;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_COLOR_MODE);
+    }
+
+    @Test
+    public void testDiff_hdrSdrRatio() {
+        var oldDdi = createInfo();
+        var newDdi = createInfo();
+
+        /* First change new ratio to non-NaN */
+        newDdi.hdrSdrRatio = 2.3f;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+
+        /* Then change old to be non-NaN and also distinct */
+        oldDdi.hdrSdrRatio = 1.1f;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+
+        /* Now make the new one NaN and the old one non-NaN */
+        newDdi.hdrSdrRatio = Float.NaN;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_HDR_SDR_RATIO);
+    }
+
+    @Test
+    public void testDiff_rotation() {
+        var oldDdi = createInfo();
+        var newDdi = createInfo();
+
+        newDdi.rotation = Surface.ROTATION_270;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_ROTATION);
+    }
+
+    @Test
+    public void testDiff_frameRate() {
+        var oldDdi = createInfo();
+        var newDdi = createInfo();
+
+        newDdi.renderFrameRate = 123.4f;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+        newDdi.renderFrameRate = oldDdi.renderFrameRate;
+
+        newDdi.appVsyncOffsetNanos = 31222221;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+        newDdi.appVsyncOffsetNanos = oldDdi.appVsyncOffsetNanos;
+
+        newDdi.presentationDeadlineNanos = 23000000;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_RENDER_TIMINGS);
+    }
+
+    @Test
+    public void testDiff_modeId() {
+        var oldDdi = createInfo();
+        var newDdi = createInfo();
+
+        newDdi.modeId = 9;
+        assertThat(oldDdi.diff(newDdi)).isEqualTo(DIFF_MODE_ID);
+    }
+
+    private static DisplayDeviceInfo createInfo() {
+        var ddi = new DisplayDeviceInfo();
+        ddi.name = "TestDisplayDeviceInfo";
+        ddi.uniqueId = "test:51651561321";
+        ddi.width = 671;
+        ddi.height = 483;
+        ddi.modeId = 2;
+        ddi.renderFrameRate = 68.9f;
+        ddi.supportedModes = new Display.Mode[] {
+                new Display.Mode.Builder().setRefreshRate(68.9f).setResolution(671, 483).build(),
+        };
+        ddi.appVsyncOffsetNanos = 6233332;
+        ddi.presentationDeadlineNanos = 11500000;
+        ddi.rotation = Surface.ROTATION_90;
+        ddi.state = Display.STATE_ON;
+        ddi.committedState = Display.STATE_DOZE_SUSPEND;
+        return ddi;
+    }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index d0eb83a..211ab03 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -2569,6 +2569,63 @@
     }
 
     @Test
+    public void testPowerOnAndOffInternalDisplay() {
+        manageDisplaysPermission(/* granted= */ true);
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        DisplayManagerService.BinderService bs = displayManager.new BinderService();
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+        bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+
+        callback.expectsEvent(EVENT_DISPLAY_ADDED);
+        FakeDisplayDevice displayDevice =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        callback.waitForExpectedEvent();
+
+        LogicalDisplay display =
+                logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+                .isEqualTo(Display.STATE_ON);
+
+        assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), false))
+                .isTrue();
+
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+                .isEqualTo(Display.STATE_OFF);
+
+        assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), true))
+                .isTrue();
+
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+                .isEqualTo(Display.STATE_ON);
+    }
+
+    @Test
+    public void testPowerOnAndOffInternalDisplay_withoutPermission_shouldThrowException() {
+        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+        DisplayManagerService.BinderService bs = displayManager.new BinderService();
+        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+        FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+        bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+
+        callback.expectsEvent(EVENT_DISPLAY_ADDED);
+        FakeDisplayDevice displayDevice =
+                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+        callback.waitForExpectedEvent();
+
+        LogicalDisplay display =
+                logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+        var displayId = display.getDisplayIdLocked();
+
+        assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState)
+                .isEqualTo(Display.STATE_ON);
+
+        assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, true));
+        assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, false));
+    }
+
+    @Test
     public void testEnableExternalDisplay_withDisplayManagement_shouldSignalDisplayAdded() {
         when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         manageDisplaysPermission(/* granted= */ true);
@@ -3529,6 +3586,7 @@
 
         public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) {
             mDisplayDeviceInfo = displayDeviceInfo;
+            mDisplayDeviceInfo.committedState = Display.STATE_ON;
         }
 
         @Override
@@ -3558,5 +3616,14 @@
         public Display.Mode getUserPreferredDisplayModeLocked() {
             return mPreferredMode;
         }
+
+        @Override
+        public Runnable requestDisplayStateLocked(
+                final int state,
+                final float brightnessState,
+                final float sdrBrightnessState,
+                @Nullable DisplayOffloadSessionImpl displayOffloadSession) {
+            return () -> mDisplayDeviceInfo.committedState = state;
+        }
     }
 }
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 8fd1e6b..95f0b65 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -39,6 +39,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
@@ -1284,14 +1285,37 @@
 
     @Test
     public void testDwbcCallsHappenOnHandler() {
+        when(mDisplayManagerFlagsMock.isAdaptiveTone1Enabled()).thenReturn(true);
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
 
-        mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
-        verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true);
+        // Send a display power request
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+        dpr.useProximitySensor = true;
+        mHolder.dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+        // Run updatePowerState
+        advanceTime(1);
+
+        setUpDisplay(DISPLAY_ID, "new_unique_id", mHolder.display, mock(DisplayDevice.class),
+                mHolder.config, /* isEnabled= */ true);
 
         // dispatch handler looper
         advanceTime(1);
-        verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true);
+        clearInvocations(mDisplayWhiteBalanceControllerMock, mHolder.automaticBrightnessController,
+                mHolder.animator);
+        mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
+        verifyNoMoreInteractions(mDisplayWhiteBalanceControllerMock,
+                mHolder.automaticBrightnessController,
+                mHolder.animator);
+
+        // dispatch handler looper
+        advanceTime(1);
+        verify(mHolder.automaticBrightnessController).switchMode(AUTO_BRIGHTNESS_MODE_IDLE,
+                /* sendUpdate= */ true);
+        verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
+                BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
+        verify(mDisplayWhiteBalanceControllerMock).setStrongModeEnabled(true);
     }
 
     @Test
@@ -1391,7 +1415,7 @@
         when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
         when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
-        when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+        when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
                 invocation -> DisplayBrightnessState.builder()
                         .setIsSlowChange(invocation.getArgument(2))
                         .setBrightness(invocation.getArgument(1))
@@ -1415,7 +1439,7 @@
         when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
         when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f);
         when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f);
-        when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+        when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
                 invocation -> DisplayBrightnessState.builder()
                         .setIsSlowChange(invocation.getArgument(2))
                         .setBrightness(invocation.getArgument(1))
@@ -1506,6 +1530,9 @@
         // switch to idle mode
         mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
 
+        // Process the MSG_SWITCH_AUTOBRIGHTNESS_MODE event
+        advanceTime(1);
+
         // A second time when switching to idle mode.
         verify(mHolder.animator, times(2)).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX,
                 BRIGHTNESS_RAMP_DECREASE_MAX);
@@ -1532,6 +1559,8 @@
         // switch to idle mode
         mHolder.dpc.setAutomaticScreenBrightnessMode(AUTO_BRIGHTNESS_MODE_IDLE);
 
+        // Process the MSG_SWITCH_AUTOBRIGHTNESS_MODE event
+        advanceTime(1);
         verify(mHolder.animator).setAnimationTimeLimits(BRIGHTNESS_RAMP_INCREASE_MAX_IDLE,
                 BRIGHTNESS_RAMP_DECREASE_MAX_IDLE);
     }
@@ -2065,7 +2094,7 @@
         BrightnessClamperController clamperController = mock(BrightnessClamperController.class);
 
         when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX);
-        when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer(
+        when(clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer(
                 invocation -> DisplayBrightnessState.builder()
                         .setIsSlowChange(invocation.getArgument(2))
                         .setBrightness(invocation.getArgument(1))
@@ -2299,7 +2328,7 @@
         BrightnessClamperController getBrightnessClamperController(Handler handler,
                 BrightnessClamperController.ClamperChangeListener clamperChangeListener,
                 BrightnessClamperController.DisplayDeviceData data, Context context,
-                DisplayManagerFlags flags) {
+                DisplayManagerFlags flags, SensorManager sensorManager) {
             return mClamperController;
         }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 5487bc5..69043f5 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -16,14 +16,20 @@
 
 package com.android.server.display.brightness.clamper;
 
+import static android.view.Display.STATE_ON;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Handler;
@@ -34,11 +40,15 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.TestUtils;
 import com.android.server.display.brightness.BrightnessReason;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.display.feature.DisplayManagerFlags;
+import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
 
 import org.junit.Before;
@@ -54,13 +64,16 @@
 public class BrightnessClamperControllerTest {
     private static final float FLOAT_TOLERANCE = 0.001f;
 
-    private final TestHandler mTestHandler = new TestHandler(null);
+    private final OffsettableClock mClock = new OffsettableClock();
+    private final TestHandler mTestHandler = new TestHandler(null, mClock);
 
     @Rule
     public final TestableContext mMockContext = new TestableContext(
             InstrumentationRegistry.getInstrumentation().getContext());
     @Mock
     private BrightnessClamperController.ClamperChangeListener mMockExternalListener;
+    @Mock
+    private SensorManager mSensorManager;
 
     @Mock
     private BrightnessClamperController.DisplayDeviceData mMockDisplayDeviceData;
@@ -74,15 +87,25 @@
     private BrightnessModifier mMockModifier;
     @Mock
     private DisplayManagerInternal.DisplayPowerRequest mMockRequest;
+
+    Sensor mLightSensor;
+
     @Mock
     private DeviceConfig.Properties mMockProperties;
     private BrightnessClamperController mClamperController;
     private TestInjector mTestInjector;
 
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
         mTestInjector = new TestInjector(List.of(mMockClamper), List.of(mMockModifier));
+        when(mSensorManager.getDefaultSensor(anyInt())).thenReturn(mLightSensor);
+        when(mMockModifier.shouldListenToLightSensor()).thenReturn(true);
+
         mClamperController = createBrightnessClamperController();
     }
 
@@ -132,7 +155,7 @@
     public void testClamp_AppliesModifier() {
         float initialBrightness = 0.2f;
         boolean initialSlowChange = true;
-        mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange);
+        mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_ON);
 
         verify(mMockModifier).apply(eq(mMockRequest), any());
     }
@@ -151,7 +174,7 @@
         mTestHandler.flush();
 
         DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
-                initialSlowChange);
+                initialSlowChange, STATE_ON);
 
         assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
         assertEquals(PowerManager.BRIGHTNESS_MAX, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -175,7 +198,7 @@
         mTestHandler.flush();
 
         DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
-                initialSlowChange);
+                initialSlowChange, STATE_ON);
 
         assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE);
         assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -199,7 +222,7 @@
         mTestHandler.flush();
 
         DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
-                initialSlowChange);
+                initialSlowChange, STATE_ON);
 
         assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
         assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -223,10 +246,10 @@
         mTestHandler.flush();
         // first call of clamp method
         mClamperController.clamp(mMockRequest, initialBrightness,
-                initialSlowChange);
+                initialSlowChange, STATE_ON);
         // immediately second call of clamp method
         DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
-                initialSlowChange);
+                initialSlowChange, STATE_ON);
 
         assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE);
         assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE);
@@ -237,6 +260,31 @@
     }
 
     @Test
+    public void testAmbientLuxChanges() throws Exception {
+        ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass(
+                SensorEventListener.class);
+
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                anyInt(), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL))).thenReturn(List.of(mLightSensor));
+
+        float initialBrightness = 0.8f;
+        boolean initialSlowChange = true;
+
+        DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness,
+                initialSlowChange, STATE_ON);
+        assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE);
+
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 50, mClock.now()));
+        verify(mMockModifier).setAmbientLux(50);
+
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 300, mClock.now()));
+        verify(mMockModifier).setAmbientLux(300);
+    }
+
+    @Test
     public void testStop() {
         mClamperController.stop();
         verify(mMockModifier).stop();
@@ -245,7 +293,7 @@
 
     private BrightnessClamperController createBrightnessClamperController() {
         return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener,
-                mMockDisplayDeviceData, mMockContext, mFlags);
+                mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager);
     }
 
     private class TestInjector extends BrightnessClamperController.Injector {
@@ -282,7 +330,7 @@
         @Override
         List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context,
                 Handler handler, BrightnessClamperController.ClamperChangeListener listener,
-                DisplayDeviceConfig displayDeviceConfig) {
+                DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) {
             return mModifiers;
         }
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
index 21e066d..d672435 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt
@@ -20,8 +20,6 @@
 import android.provider.Settings
 import android.testing.TestableContext
 import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED
-import com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
 import com.android.server.display.DisplayDeviceConfig
 import com.android.server.display.brightness.BrightnessReason
 import com.android.server.display.feature.flags.Flags
@@ -54,10 +52,12 @@
     @Before
     fun setUp() {
         modifier =
-            BrightnessLowLuxModifier(testHandler,
+            BrightnessLowLuxModifier(
+                testHandler,
                 mockClamperChangeListener,
                 context,
-                mockDisplayDeviceConfig)
+                mockDisplayDeviceConfig
+            )
 
         // values below transition point (even dimmer range)
         // nits: 0.1 -> backlight 0.02 -> brightness -> 0.1
@@ -66,7 +66,7 @@
         whenever(mockDisplayDeviceConfig.getBrightnessFromBacklight(/* backlight = */ 0.02f))
                 .thenReturn(LOW_LUX_BRIGHTNESS)
 
-        // values above transition point (noraml range)
+        // values above transition point (normal range)
         // nits: 10 -> backlight 0.2 -> brightness -> 0.3
         whenever(mockDisplayDeviceConfig.getBacklightFromNits(/* nits= */ 2f))
                 .thenReturn(0.15f)
@@ -95,12 +95,12 @@
         // test transition point ensures brightness doesn't drop when setting is off.
         Settings.Secure.putIntForUser(context.contentResolver,
             Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID)
-        modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
         modifier.recalculateLowerBound()
         testHandler.flush()
         assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT)
         assertThat(modifier.brightnessReason).isEqualTo(0) // no reason - ie off
-        modifier.onAmbientLuxChange(3000.0f)
+        modifier.setAmbientLux(3000f)
+
         testHandler.flush()
         assertThat(modifier.isActive).isFalse()
         assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT)
@@ -115,8 +115,8 @@
             Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID)
         Settings.Secure.putFloatForUser(context.contentResolver,
             Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.1f, USER_ID)
-        modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
-        modifier.onAmbientLuxChange(400.0f)
+        modifier.setAmbientLux(400f)
+
         testHandler.flush()
 
         assertThat(modifier.isActive).isTrue()
@@ -133,7 +133,6 @@
             Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID)
         Settings.Secure.putFloatForUser(context.contentResolver,
             Settings.Secure.EVEN_DIMMER_MIN_NITS, 10.0f, USER_ID)
-        modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
         modifier.recalculateLowerBound()
         testHandler.flush()
 
@@ -152,8 +151,8 @@
             Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on
         Settings.Secure.putFloatForUser(context.contentResolver,
             Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID)
-        modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
-        modifier.onAmbientLuxChange(400.0f)
+        modifier.setAmbientLux(400f)
+
         testHandler.flush()
 
         assertThat(modifier.isActive).isTrue()
@@ -180,8 +179,8 @@
             Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID) // off
         Settings.Secure.putFloatForUser(context.contentResolver,
             Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID)
-        modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
-        modifier.onAmbientLuxChange(400.0f)
+        modifier.setAmbientLux(400f)
+
         testHandler.flush()
 
         assertThat(modifier.isActive).isFalse()
@@ -203,15 +202,14 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_EVEN_DIMMER)
-    fun testDisabledWhenAutobrightnessIsOff() {
+    fun testEnabledEvenWhenAutobrightnessIsOff() {
         // test that high lux prevents low brightness range.
         Settings.Secure.putIntForUser(context.contentResolver,
             Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on
         Settings.Secure.putFloatForUser(context.contentResolver,
             Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID)
 
-        modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED)
-        modifier.onAmbientLuxChange(400.0f)
+        modifier.setAmbientLux(400f)
         testHandler.flush()
 
         assertThat(modifier.isActive).isTrue()
@@ -219,15 +217,13 @@
         assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX)
         assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS)
 
-
-        modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_DISABLED)
-        modifier.onAmbientLuxChange(400.0f)
+        modifier.setAmbientLux(400f)
         testHandler.flush()
 
-        assertThat(modifier.isActive).isFalse()
+        assertThat(modifier.isActive).isTrue()
         // Test restriction from lux setting
-        assertThat(modifier.brightnessReason).isEqualTo(0)
-        assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT)
+        assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX)
+        assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS)
     }
 }
 
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 8a33f34..1d04baa 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
@@ -570,6 +570,86 @@
         assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
     }
 
+    @Test
+    public void
+            updateBrightness_constructsDisplayBrightnessState_withNoAdjustmentFlag_isSlowChange() {
+        BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
+        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
+                mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
+        mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
+                mAutomaticBrightnessController);
+        float brightness = 0.4f;
+        BrightnessReason brightnessReason = new BrightnessReason();
+        brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
+        when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent))
+                .thenReturn(brightness);
+
+        // Set the state such that auto-brightness was already applied
+        mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true);
+
+        // Update the auto-brightess validity state to change the isSlowChange flag
+        mAutomaticBrightnessStrategy.isAutoBrightnessValid();
+
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                mock(DisplayManagerInternal.DisplayPowerRequest.class);
+
+        DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder()
+                .setBrightness(brightness)
+                .setSdrBrightness(brightness)
+                .setBrightnessReason(brightnessReason)
+                .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName())
+                .setIsSlowChange(true)
+                .setBrightnessEvent(brightnessEvent)
+                .setBrightnessAdjustmentFlag(0)
+                .setShouldUpdateScreenBrightnessSetting(true)
+                .setIsUserInitiatedChange(true)
+                .build();
+        DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
+                .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+                        /* userSetBrightnessChanged= */ true));
+        assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
+    }
+
+
+    @Test
+    public void updateBrightness_autoBrightnessNotApplied_noAdjustments_isNotSlowChange() {
+        BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
+        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
+                mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
+        mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
+                mAutomaticBrightnessController);
+        float brightness = 0.4f;
+        BrightnessReason brightnessReason = new BrightnessReason();
+        brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
+        when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent))
+                .thenReturn(brightness);
+
+        // Set the state such that auto-brightness was not already applied
+        mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false);
+
+        // Update the auto-brightess validity state to change the isSlowChange flag
+        mAutomaticBrightnessStrategy.isAutoBrightnessValid();
+
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                mock(DisplayManagerInternal.DisplayPowerRequest.class);
+
+        DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder()
+                .setBrightness(brightness)
+                .setSdrBrightness(brightness)
+                .setBrightnessReason(brightnessReason)
+                .setDisplayBrightnessStrategyName(mAutomaticBrightnessStrategy.getName())
+                .setIsSlowChange(false)
+                .setBrightnessEvent(brightnessEvent)
+                .setBrightnessAdjustmentFlag(0)
+                .setShouldUpdateScreenBrightnessSetting(true)
+                .setIsUserInitiatedChange(true)
+                .build();
+        DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
+                .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+                        /* userSetBrightnessChanged= */ true));
+        assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
+    }
+
     private void setPendingAutoBrightnessAdjustment(float pendingAutoBrightnessAdjustment) {
         Settings.System.putFloat(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingAutoBrightnessAdjustment);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
index 88c0daa..95702aa 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
@@ -19,11 +19,12 @@
 import android.content.Context
 import android.content.ContextWrapper
 import android.hardware.display.BrightnessInfo
-import android.util.SparseArray
 import android.view.Display
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.filters.SmallTest
 import com.android.server.display.DisplayDeviceConfig
+import com.android.server.display.config.RefreshRateData
+import com.android.server.display.config.SupportedModeData
 import com.android.server.display.feature.DisplayManagerFlags
 import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
 import com.android.server.testutils.TestHandler
@@ -39,6 +40,13 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
+private val LOW_LIGHT_REFRESH_RATE_DATA = createRefreshRateData(
+    lowLightBlockingZoneSupportedModes = listOf(
+        SupportedModeData(60f, 60f), SupportedModeData(240f, 240f)))
+private val EXPECTED_SUPPORTED_MODES_VOTE = SupportedRefreshRatesVote(
+    listOf(SupportedRefreshRatesVote.RefreshRates(60f, 60f),
+        SupportedRefreshRatesVote.RefreshRates(240f, 240f)))
+
 @SmallTest
 @RunWith(TestParameterInjector::class)
 class BrightnessObserverTest {
@@ -65,21 +73,25 @@
         whenever(mockFlags.isVsyncLowLightVoteEnabled).thenReturn(testCase.vsyncLowLightVoteEnabled)
         val displayModeDirector = DisplayModeDirector(
                 spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
-        val ddcByDisplay = SparseArray<DisplayDeviceConfig>()
         whenever(mockDeviceConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
-        ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig)
-        displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay)
+        whenever(mockDeviceConfig.refreshRateData).thenReturn(testCase.refreshRateData)
+        whenever(mockDeviceConfig.defaultLowBlockingZoneRefreshRate).thenReturn(-1)
+
+        displayModeDirector.defaultDisplayDeviceUpdated(mockDeviceConfig)
+
         val brightnessObserver = displayModeDirector.BrightnessObserver(
                 spyContext, testHandler, mockInjector, mockFlags)
-
+        // set mRefreshRateChangeable to true
         brightnessObserver.onRefreshRateSettingChangedLocked(0.0f, 120.0f)
         brightnessObserver.updateBlockingZoneThresholds(mockDeviceConfig, false)
-        brightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(60)
+        brightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(testCase.refreshRateInLowZone)
 
         brightnessObserver.onDisplayChanged(Display.DEFAULT_DISPLAY)
 
         assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID,
-                Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH)).isEqualTo(testCase.expectedVote)
+            Vote.PRIORITY_FLICKER_REFRESH_RATE)).isEqualTo(testCase.expectedRefreshRateVote)
+        assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID,
+                Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH)).isEqualTo(testCase.expectedSwitchVote)
     }
 
     private fun setUpLowBrightnessZone() {
@@ -98,14 +110,20 @@
     enum class LowLightTestCase(
             val vrrSupported: Boolean,
             val vsyncLowLightVoteEnabled: Boolean,
-            internal val expectedVote: Vote
+            val refreshRateData: RefreshRateData,
+            val refreshRateInLowZone: Int,
+            internal val expectedRefreshRateVote: Vote,
+            internal val expectedSwitchVote: Vote?,
     ) {
-        ALL_ENABLED(true, true, CombinedVote(
-                listOf(DisableRefreshRateSwitchingVote(true),
-                        SupportedRefreshRatesVote(
-                                listOf(SupportedRefreshRatesVote.RefreshRates(60f, 60f),
-                                        SupportedRefreshRatesVote.RefreshRates(120f, 120f)))))),
-        VRR_NOT_SUPPORTED(false, true, DisableRefreshRateSwitchingVote(true)),
-        VSYNC_VOTE_DISABLED(true, false, DisableRefreshRateSwitchingVote(true))
+        ALL_ENABLED(true, true, LOW_LIGHT_REFRESH_RATE_DATA, 60,
+            EXPECTED_SUPPORTED_MODES_VOTE, null),
+        ALL_ENABLED_NO_RR_IN_LOW_ZONE(true, true, LOW_LIGHT_REFRESH_RATE_DATA, 0,
+            EXPECTED_SUPPORTED_MODES_VOTE, null),
+        VRR_NOT_SUPPORTED(false, true, LOW_LIGHT_REFRESH_RATE_DATA, 60,
+            Vote.forPhysicalRefreshRates(60f, 60f), DisableRefreshRateSwitchingVote(true)),
+        VSYNC_VOTE_DISABLED(true, false, LOW_LIGHT_REFRESH_RATE_DATA, 50,
+            Vote.forPhysicalRefreshRates(50f, 50f), DisableRefreshRateSwitchingVote(true)),
+        NO_LOW_LIGHT_CONFIG(true, true, createRefreshRateData(), 40,
+            Vote.forPhysicalRefreshRates(40f, 40f), DisableRefreshRateSwitchingVote(true)),
     }
 }
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 714b423..242d559 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -132,7 +132,8 @@
             /* defaultPeakRefreshRate= */ 0,
             /* defaultRefreshRateInHbmHdr= */ 0,
             /* defaultRefreshRateInHbmSunlight= */ 0,
-            /* lowPowerSupportedModes =*/ List.of());
+            /* lowPowerSupportedModes= */ List.of(),
+            /* lowLightBlockingZoneSupportedModes= */ List.of());
 
     public static Collection<Object[]> getAppRequestedSizeTestCases() {
         var appRequestedSizeTestCases = Arrays.asList(new Object[][] {
@@ -3170,7 +3171,8 @@
                 /* defaultPeakRefreshRate= */ 65,
                 /* defaultRefreshRateInHbmHdr= */ 65,
                 /* defaultRefreshRateInHbmSunlight= */ 75,
-                /* lowPowerSupportedModes= */ List.of());
+                /* lowPowerSupportedModes= */ List.of(),
+                /* lowLightBlockingZoneSupportedModes= */ List.of());
         when(displayDeviceConfig.getRefreshRateData()).thenReturn(refreshRateData);
         when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
         when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
index 1206e30..5b07166 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
@@ -34,9 +34,10 @@
         defaultPeakRefreshRate: Int = 60,
         defaultRefreshRateInHbmHdr: Int = 60,
         defaultRefreshRateInHbmSunlight: Int = 60,
-        lowPowerSupportedModes: List<SupportedModeData> = emptyList()
+        lowPowerSupportedModes: List<SupportedModeData> = emptyList(),
+        lowLightBlockingZoneSupportedModes: List<SupportedModeData> = emptyList()
 ): RefreshRateData {
         return RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
                 defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
-                lowPowerSupportedModes)
+                lowPowerSupportedModes, lowLightBlockingZoneSupportedModes)
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index a248d6de..0125ddb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
@@ -38,10 +38,10 @@
 @RunWith(AndroidJUnit4.class)
 public class VotesStorageTest {
     private static final int DISPLAY_ID = 100;
-    private static final int PRIORITY = Vote.PRIORITY_APP_REQUEST_SIZE;
+    private static final @Vote.Priority int PRIORITY = Vote.PRIORITY_APP_REQUEST_SIZE;
     private static final Vote VOTE = Vote.forDisableRefreshRateSwitching();
     private static final int DISPLAY_ID_OTHER = 101;
-    private static final int PRIORITY_OTHER = Vote.PRIORITY_FLICKER_REFRESH_RATE;
+    private static final @Vote.Priority int PRIORITY_OTHER = Vote.PRIORITY_FLICKER_REFRESH_RATE;
     private static final Vote VOTE_OTHER = Vote.forBaseModeRefreshRate(10f);
 
     @Mock
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 e88e28b..ee96c2a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -74,6 +74,15 @@
     private static final String TAG = ApplicationStartInfoTest.class.getSimpleName();
     private static final ComponentName COMPONENT = new ComponentName("com.android.test", ".Foo");
 
+    private static final int APP_1_UID = 10123;
+    private static final int APP_1_PID_1 = 12345;
+    private static final int APP_1_PID_2 = 12346;
+    private static final int APP_1_DEFINING_UID = 23456;
+    private static final int APP_1_UID_USER_2 = 1010123;
+    private static final int APP_1_PID_USER_2 = 12347;
+    private static final String APP_1_PROCESS_NAME = "com.android.test.stub1:process";
+    private static final String APP_1_PACKAGE_NAME = "com.android.test.stub1";
+
     @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
     @Mock private AppOpsService mAppOpsService;
     @Mock private PackageManagerInternal mPackageManagerInt;
@@ -111,6 +120,12 @@
         // Remove stale instance of PackageManagerInternal if there is any
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
+
+        mAppStartInfoTracker.clearProcessStartInfo(true);
+        mAppStartInfoTracker.mAppStartInfoLoaded.set(true);
+        mAppStartInfoTracker.mAppStartInfoHistoryListSize =
+                mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE;
+        doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean());
     }
 
     @After
@@ -120,26 +135,12 @@
 
     @Test
     public void testApplicationStartInfo() throws Exception {
-        mAppStartInfoTracker.clearProcessStartInfo(true);
-        mAppStartInfoTracker.mAppStartInfoLoaded.set(true);
-        mAppStartInfoTracker.mAppStartInfoHistoryListSize =
-                mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE;
         mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(),
                 AppStartInfoTracker.APP_START_STORE_DIR);
         assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir));
         mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir,
                 AppStartInfoTracker.APP_START_INFO_FILE);
 
-        doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean());
-
-        final int app1Uid = 10123;
-        final int app1Pid1 = 12345;
-        final int app1Pid2 = 12346;
-        final int app1DefiningUid = 23456;
-        final int app1UidUser2 = 1010123;
-        final int app1PidUser2 = 12347;
-        final String app1ProcessName = "com.android.test.stub1:process";
-        final String app1PackageName = "com.android.test.stub1";
         final long appStartTimestampIntentStarted = 1000000;
         final long appStartTimestampActivityLaunchFinished = 2000000;
         final long appStartTimestampFirstFrameDrawn = 2500000;
@@ -149,23 +150,23 @@
         final long appStartTimestampRContentProvider = 6000000;
 
         ProcessRecord app = makeProcessRecord(
-                app1Pid1,                    // pid
-                app1Uid,                     // uid
-                app1Uid,                     // packageUid
+                APP_1_PID_1,                 // pid
+                APP_1_UID,                   // uid
+                APP_1_UID,                   // packageUid
                 null,                        // definingUid
-                app1ProcessName,             // processName
-                app1PackageName);            // packageName
+                APP_1_PROCESS_NAME,          // processName
+                APP_1_PACKAGE_NAME);         // packageName
 
         ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
 
         // Case 1: Activity start intent failed
         mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
                 appStartTimestampIntentStarted);
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(1);
         assertEquals(list.size(), 0);
 
-        verifyInProgApplicationStartInfo(
+        verifyInProgressApplicationStartInfo(
                 0,                                                    // index
                 0,                                                    // pid
                 0,                                                    // uid
@@ -179,7 +180,7 @@
 
         mAppStartInfoTracker.onIntentFailed(appStartTimestampIntentStarted);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(0);
         assertEquals(list.size(), 0);
 
@@ -189,24 +190,24 @@
         mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
                 appStartTimestampIntentStarted);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(1);
         assertEquals(list.size(), 0);
 
         mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT,
                 ApplicationStartInfo.START_TYPE_COLD, app);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(1);
         assertEquals(list.size(), 1);
 
-        verifyInProgApplicationStartInfo(
+        verifyInProgressApplicationStartInfo(
                 0,                                                    // index
-                app1Pid1,                                             // pid
-                app1Uid,                                              // uid
-                app1Uid,                                              // packageUid
+                APP_1_PID_1,                                          // pid
+                APP_1_UID,                                            // uid
+                APP_1_UID,                                            // packageUid
                 null,                                                 // definingUid
-                app1ProcessName,                                      // processName
+                APP_1_PROCESS_NAME,                                   // processName
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
@@ -214,17 +215,17 @@
 
         mAppStartInfoTracker.onActivityLaunchCancelled(appStartTimestampIntentStarted);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(0);
         assertEquals(list.size(), 1);
 
         verifyApplicationStartInfo(
                 list.get(0),                                          // info
-                app1Pid1,                                             // pid
-                app1Uid,                                              // uid
-                app1Uid,                                              // packageUid
+                APP_1_PID_1,                                          // pid
+                APP_1_UID,                                            // uid
+                APP_1_UID,                                            // packageUid
                 null,                                                 // definingUid
-                app1ProcessName,                                      // processName
+                APP_1_PROCESS_NAME,                                   // processName
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_ERROR,             // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
@@ -236,24 +237,24 @@
         mAppStartInfoTracker.onIntentStarted(buildIntent(COMPONENT),
                 appStartTimestampIntentStarted);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(1);
         assertEquals(list.size(), 0);
 
         mAppStartInfoTracker.onActivityLaunched(appStartTimestampIntentStarted, COMPONENT,
                 ApplicationStartInfo.START_TYPE_COLD, app);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(1);
         assertEquals(list.size(), 1);
 
-        verifyInProgApplicationStartInfo(
+        verifyInProgressApplicationStartInfo(
                 0,                                                    // index
-                app1Pid1,                                             // pid
-                app1Uid,                                              // uid
-                app1Uid,                                              // packageUid
+                APP_1_PID_1,                                          // pid
+                APP_1_UID,                                            // uid
+                APP_1_UID,                                            // packageUid
                 null,                                                 // definingUid
-                app1ProcessName,                                      // processName
+                APP_1_PROCESS_NAME,                                   // processName
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
@@ -261,11 +262,11 @@
 
         verifyApplicationStartInfo(
                 list.get(0),                                          // info
-                app1Pid1,                                             // pid
-                app1Uid,                                              // uid
-                app1Uid,                                              // packageUid
+                APP_1_PID_1,                                          // pid
+                APP_1_UID,                                            // uid
+                APP_1_UID,                                            // packageUid
                 null,                                                 // definingUid
-                app1ProcessName,                                      // processName
+                APP_1_PROCESS_NAME,                                   // processName
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
@@ -273,20 +274,20 @@
 
         mAppStartInfoTracker.onActivityLaunchFinished(appStartTimestampIntentStarted, COMPONENT,
                 appStartTimestampActivityLaunchFinished, ApplicationStartInfo.LAUNCH_MODE_STANDARD);
-        mAppStartInfoTracker.addTimestampToStart(app1PackageName, app1Uid,
+        mAppStartInfoTracker.addTimestampToStart(APP_1_PACKAGE_NAME, APP_1_UID,
                 appStartTimestampFirstFrameDrawn, ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(1);
         assertEquals(list.size(), 1);
 
-        verifyInProgApplicationStartInfo(
+        verifyInProgressApplicationStartInfo(
                 0,                                                    // index
-                app1Pid1,                                             // pid
-                app1Uid,                                              // uid
-                app1Uid,                                              // packageUid
+                APP_1_PID_1,                                          // pid
+                APP_1_UID,                                            // uid
+                APP_1_UID,                                            // packageUid
                 null,                                                 // definingUid
-                app1ProcessName,                                      // processName
+                APP_1_PROCESS_NAME,                                   // processName
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
@@ -295,17 +296,17 @@
         mAppStartInfoTracker.onReportFullyDrawn(appStartTimestampIntentStarted,
                 appStartTimestampReportFullyDrawn);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_1, 0, list);
         verifyInProgressRecordsSize(0);
         assertEquals(list.size(), 1);
 
         verifyApplicationStartInfo(
                 list.get(0),                                          // info
-                app1Pid1,                                             // pid
-                app1Uid,                                              // uid
-                app1Uid,                                              // packageUid
+                APP_1_PID_1,                                          // pid
+                APP_1_UID,                                            // uid
+                APP_1_UID,                                            // packageUid
                 null,                                                 // definingUid
-                app1ProcessName,                                      // processName
+                APP_1_PROCESS_NAME,                                   // processName
                 ApplicationStartInfo.START_REASON_START_ACTIVITY,     // reason
                 ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN, // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
@@ -316,26 +317,26 @@
         // Case 4: Create an other app1 record with different pid started for a service
         sleep(1);
         app = makeProcessRecord(
-                app1Pid2,                    // pid
-                app1Uid,                     // uid
-                app1Uid,                     // packageUid
-                app1DefiningUid,             // definingUid
-                app1ProcessName,             // processName
-                app1PackageName);            // packageName
+                APP_1_PID_2,                 // pid
+                APP_1_UID,                   // uid
+                APP_1_UID,                   // packageUid
+                APP_1_DEFINING_UID,          // definingUid
+                APP_1_PROCESS_NAME,          // processName
+                APP_1_PACKAGE_NAME);         // packageName
         ServiceRecord service = ServiceRecord.newEmptyInstanceForTest(mAms);
 
         mAppStartInfoTracker.handleProcessServiceStart(appStartTimestampService, app, service);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, 0, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, 0, 0, list);
         assertEquals(list.size(), 2);
 
         verifyApplicationStartInfo(
                 list.get(0),                                          // info
-                app1Pid2,                                             // pid
-                app1Uid,                                              // uid
-                app1Uid,                                              // packageUid
-                app1DefiningUid,                                      // definingUid
-                app1ProcessName,                                      // processName
+                APP_1_PID_2,                                          // pid
+                APP_1_UID,                                            // uid
+                APP_1_UID,                                            // packageUid
+                APP_1_DEFINING_UID,                                   // definingUid
+                APP_1_PROCESS_NAME,                                   // processName
                 ApplicationStartInfo.START_REASON_SERVICE,            // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
@@ -344,39 +345,41 @@
         // Case 5: Create an instance of app1 with a different user started for a broadcast
         sleep(1);
         app = makeProcessRecord(
-                app1PidUser2,                    // pid
-                app1UidUser2,                    // uid
-                app1UidUser2,                    // packageUid
+                APP_1_PID_USER_2,                // pid
+                APP_1_UID_USER_2,                // uid
+                APP_1_UID_USER_2,                // packageUid
                 null,                            // definingUid
-                app1ProcessName,                 // processName
-                app1PackageName);                // packageName
+                APP_1_PROCESS_NAME,              // processName
+                APP_1_PACKAGE_NAME);             // packageName
 
         mAppStartInfoTracker.handleProcessBroadcastStart(appStartTimestampBroadcast, app,
                 buildIntent(COMPONENT), false /* isAlarm */);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, APP_1_PID_USER_2, 0,
+                list);
         assertEquals(list.size(), 1);
 
         verifyApplicationStartInfo(
                 list.get(0),                                          // info
-                app1PidUser2,                                         // pid
-                app1UidUser2,                                         // uid
-                app1UidUser2,                                         // packageUid
+                APP_1_PID_USER_2,                                     // pid
+                APP_1_UID_USER_2,                                     // uid
+                APP_1_UID_USER_2,                                     // packageUid
                 null,                                                 // definingUid
-                app1ProcessName,                                      // processName
+                APP_1_PROCESS_NAME,                                   // processName
                 ApplicationStartInfo.START_REASON_BROADCAST,          // reason
                 ApplicationStartInfo.STARTUP_STATE_STARTED,           // startup state
                 ApplicationStartInfo.START_TYPE_COLD,                 // state type
                 ApplicationStartInfo.LAUNCH_MODE_STANDARD);           // launch mode
 
         // Case 6: User 2 gets removed
-        mAppStartInfoTracker.onPackageRemoved(app1PackageName, app1UidUser2, false);
+        mAppStartInfoTracker.onPackageRemoved(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, false);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID_USER_2, APP_1_PID_USER_2, 0,
+                list);
         assertEquals(list.size(), 0);
 
         list.clear();
-        mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1PidUser2, 0, list);
+        mAppStartInfoTracker.getStartInfo(APP_1_PACKAGE_NAME, APP_1_UID, APP_1_PID_USER_2, 0, list);
         assertEquals(list.size(), 2);
 
 
@@ -416,7 +419,7 @@
 
         // Case 8: Save and load again
         ArrayList<ApplicationStartInfo> original = new ArrayList<ApplicationStartInfo>();
-        mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, original);
+        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, original);
         assertTrue(original.size() > 0);
 
         mAppStartInfoTracker.persistProcessStartInfo();
@@ -424,12 +427,12 @@
 
         mAppStartInfoTracker.clearProcessStartInfo(false);
         list.clear();
-        mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list);
+        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
         assertEquals(0, list.size());
 
         mAppStartInfoTracker.loadExistingProcessStartInfo();
         list.clear();
-        mAppStartInfoTracker.getStartInfo(null, app1Uid, 0, 0, list);
+        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
         assertEquals(original.size(), list.size());
 
         for (int i = list.size() - 1; i >= 0; i--) {
@@ -437,6 +440,48 @@
         }
     }
 
+    /**
+     * Test to make sure that in progress records stay within their size limits and discard the
+     * correct records.
+     */
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testInProgressRecordsLimit() throws Exception {
+        ProcessRecord app = makeProcessRecord(
+                APP_1_PID_1,                 // pid
+                APP_1_UID,                   // uid
+                APP_1_UID,                   // packageUid
+                null,                        // definingUid
+                APP_1_PROCESS_NAME,          // processName
+                APP_1_PACKAGE_NAME);         // packageName
+
+        // Mock performing 2 x MAX_IN_PROGRESS_RECORDS successful starts and ensure that the list
+        // 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);
+            verifyInProgressRecordsSize(
+                    Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS));
+
+            mAppStartInfoTracker.onActivityLaunched(startTime, COMPONENT,
+                    ApplicationStartInfo.START_TYPE_COLD, app);
+            verifyInProgressRecordsSize(
+                    Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS));
+
+            mAppStartInfoTracker.onActivityLaunchFinished(startTime, COMPONENT,
+                    startTime + 100, ApplicationStartInfo.LAUNCH_MODE_STANDARD);
+            verifyInProgressRecordsSize(
+                    Math.min(i + 1, AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS));
+
+            // Make sure that the record added in this iteration is still present.
+            assertTrue(mAppStartInfoTracker.mInProgressRecords.containsKey(startTime));
+        }
+
+        // Confirm that after 2 x MAX_IN_PROGRESS_RECORDS starts only MAX_IN_PROGRESS_RECORDS are
+        // present.
+        verifyInProgressRecordsSize(AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS);
+    }
+
     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
         try {
             Field field = clazz.getDeclaredField(fieldName);
@@ -484,16 +529,16 @@
 
     private void verifyInProgressRecordsSize(int expectedSize) {
         synchronized (mAppStartInfoTracker.mLock) {
-            assertEquals(mAppStartInfoTracker.mInProgRecords.size(), expectedSize);
+            assertEquals(mAppStartInfoTracker.mInProgressRecords.size(), expectedSize);
         }
     }
 
-    private void verifyInProgApplicationStartInfo(int index,
+    private void verifyInProgressApplicationStartInfo(int index,
             Integer pid, Integer uid, Integer packageUid,
             Integer definingUid, String processName,
             Integer reason, Integer startupState, Integer startType, Integer launchMode) {
         synchronized (mAppStartInfoTracker.mLock) {
-            verifyApplicationStartInfo(mAppStartInfoTracker.mInProgRecords.valueAt(index),
+            verifyApplicationStartInfo(mAppStartInfoTracker.mInProgressRecords.valueAt(index),
                     pid, uid, packageUid, definingUid, processName, reason, startupState,
                     startType, launchMode);
         }
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 28c7fb2..488ce66 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -44,6 +44,7 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG;
 import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
 import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
 import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
@@ -77,6 +78,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.doNothing;
@@ -113,11 +115,10 @@
 import com.android.server.wm.WindowProcessController;
 
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
 import java.io.File;
 import java.lang.reflect.Field;
@@ -164,92 +165,86 @@
 
     private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
             + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-    private static Context sContext;
-    private static PackageManagerInternal sPackageManagerInternal;
-    private static ActivityManagerService sService;
+    private Context mContext;
+    private PackageManagerInternal mPackageManagerInternal;
+    private ActivityManagerService mService;
+    private OomAdjusterInjector mInjector = new OomAdjusterInjector();
 
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @SuppressWarnings("GuardedBy")
-    @BeforeClass
-    public static void setUpOnce() {
-        sContext = getInstrumentation().getTargetContext();
+    @Before
+    public void setUp() {
+        mContext = getInstrumentation().getTargetContext();
         System.setProperty("dexmaker.share_classloader", "true");
 
-        sPackageManagerInternal = mock(PackageManagerInternal.class);
-        doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
+        mPackageManagerInternal = mock(PackageManagerInternal.class);
+        doReturn(new ComponentName("", "")).when(mPackageManagerInternal)
                 .getSystemUiServiceComponent();
         // Remove stale instance of PackageManagerInternal if there is any
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
 
-        sService = mock(ActivityManagerService.class);
-        sService.mActivityTaskManager = new ActivityTaskManagerService(sContext);
-        sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
-        sService.mPackageManagerInt = sPackageManagerInternal;
-        sService.mAtmInternal = spy(sService.mActivityTaskManager.getAtmInternal());
+        mService = mock(ActivityManagerService.class);
+        mService.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+        mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+        mService.mPackageManagerInt = mPackageManagerInternal;
+        mService.mAtmInternal = spy(mService.mActivityTaskManager.getAtmInternal());
 
-        sService.mConstants = new ActivityManagerConstants(sContext, sService,
-                sContext.getMainThreadHandler());
-        setFieldValue(ActivityManagerService.class, sService, "mContext",
-                sContext);
+        mService.mConstants = new ActivityManagerConstants(mContext, mService,
+                mContext.getMainThreadHandler());
+        setFieldValue(ActivityManagerService.class, mService, "mContext",
+                mContext);
         ProcessList pr = spy(new ProcessList());
-        pr.mService = sService;
+        pr.mService = mService;
         AppProfiler profiler = mock(AppProfiler.class);
-        setFieldValue(ActivityManagerService.class, sService, "mProcessList",
+        setFieldValue(ActivityManagerService.class, mService, "mProcessList",
                 pr);
-        setFieldValue(ActivityManagerService.class, sService, "mHandler",
+        setFieldValue(ActivityManagerService.class, mService, "mHandler",
                 mock(ActivityManagerService.MainHandler.class));
-        setFieldValue(ActivityManagerService.class, sService, "mProcessStats",
-                new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats")));
-        setFieldValue(ActivityManagerService.class, sService, "mBackupTargets",
+        setFieldValue(ActivityManagerService.class, mService, "mProcessStats",
+                new ProcessStatsService(mService, new File(mContext.getFilesDir(), "procstats")));
+        setFieldValue(ActivityManagerService.class, mService, "mBackupTargets",
                 mock(SparseArray.class));
-        setFieldValue(ActivityManagerService.class, sService, "mUserController",
+        setFieldValue(ActivityManagerService.class, mService, "mUserController",
                 mock(UserController.class));
-        setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
-        setFieldValue(ActivityManagerService.class, sService, "mProcLock",
+        setFieldValue(ActivityManagerService.class, mService, "mAppProfiler", profiler);
+        setFieldValue(ActivityManagerService.class, mService, "mProcLock",
                 new ActivityManagerProcLock());
-        setFieldValue(ActivityManagerService.class, sService, "mServices",
-                spy(new ActiveServices(sService)));
-        setFieldValue(ActivityManagerService.class, sService, "mInternal",
+        setFieldValue(ActivityManagerService.class, mService, "mServices",
+                spy(new ActiveServices(mService)));
+        setFieldValue(ActivityManagerService.class, mService, "mInternal",
                 mock(ActivityManagerService.LocalService.class));
-        setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService",
+        setFieldValue(ActivityManagerService.class, mService, "mBatteryStatsService",
                 mock(BatteryStatsService.class));
-        setFieldValue(ActivityManagerService.class, sService, "mInjector",
-                new ActivityManagerService.Injector(sContext));
-        doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager();
-        doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
-        doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
+        setFieldValue(ActivityManagerService.class, mService, "mInjector",
+                new ActivityManagerService.Injector(mContext));
+        doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager();
+        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());
-        sService.mOomAdjuster = sService.mConstants.ENABLE_NEW_OOMADJ
-                ? new OomAdjusterModernImpl(sService, sService.mProcessList,
-                        new ActiveUids(sService, false))
-                : new OomAdjuster(sService, sService.mProcessList, new ActiveUids(sService, false));
-        sService.mOomAdjuster.mAdjSeq = 10000;
-        sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
-        if (sService.mConstants.USE_TIERED_CACHED_ADJ) {
+        mService.mOomAdjuster = mService.mConstants.ENABLE_NEW_OOMADJ
+                ? new OomAdjusterModernImpl(mService, mService.mProcessList,
+                        new ActiveUids(mService, false), mInjector)
+                : new OomAdjuster(mService, mService.mProcessList, new ActiveUids(mService, false),
+                        mInjector);
+        mService.mOomAdjuster.mAdjSeq = 10000;
+        mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        if (mService.mConstants.USE_TIERED_CACHED_ADJ) {
             sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
         }
-    }
-
-    @Before
-    public void setUp() {
         mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
     }
 
-    @AfterClass
-    public static void tearDownOnce() {
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-    }
-
     @SuppressWarnings("GuardedBy")
     @After
     public void tearDown() {
-        sService.mOomAdjuster.resetInternal();
-        sService.mOomAdjuster.mActiveUids.clear();
+        mService.mOomAdjuster.resetInternal();
+        mService.mOomAdjuster.mActiveUids.clear();
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
     }
 
     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
@@ -278,7 +273,7 @@
      */
     @SuppressWarnings("GuardedBy")
     private void setProcessesToLru(ProcessRecord... apps) {
-        ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
+        ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         Collections.addAll(lru, apps);
     }
@@ -292,20 +287,20 @@
     @SuppressWarnings("GuardedBy")
     private void updateOomAdj(ProcessRecord... apps) {
         if (apps.length == 0) {
-            updateProcessRecordNodes(sService.mProcessList.getLruProcessesLOSP());
-            sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+            updateProcessRecordNodes(mService.mProcessList.getLruProcessesLOSP());
+            mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
         } else {
             updateProcessRecordNodes(Arrays.asList(apps));
             if (apps.length == 1) {
                 final ProcessRecord app = apps[0];
-                if (!sService.mProcessList.getLruProcessesLOSP().contains(app)) {
-                    sService.mProcessList.getLruProcessesLOSP().add(app);
+                if (!mService.mProcessList.getLruProcessesLOSP().contains(app)) {
+                    mService.mProcessList.getLruProcessesLOSP().add(app);
                 }
-                sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE);
+                mService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE);
             } else {
                 setProcessesToLru(apps);
-                sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
-                sService.mProcessList.getLruProcessesLOSP().clear();
+                mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+                mService.mProcessList.getLruProcessesLOSP().clear();
             }
         }
     }
@@ -318,10 +313,10 @@
     private void updateOomAdjPending(ProcessRecord... apps) {
         setProcessesToLru(apps);
         for (ProcessRecord app : apps) {
-            sService.mOomAdjuster.enqueueOomAdjTargetLocked(app);
+            mService.mOomAdjuster.enqueueOomAdjTargetLocked(app);
         }
-        sService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE);
-        sService.mProcessList.getLruProcessesLOSP().clear();
+        mService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE);
+        mService.mProcessList.getLruProcessesLOSP().clear();
     }
 
     /**
@@ -343,9 +338,9 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         app.mState.setHasTopUi(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
         updateOomAdj(app);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERSISTENT_PROC_ADJ,
                 SCHED_GROUP_RESTRICTED);
@@ -359,7 +354,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         app.mState.setHasTopUi(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ,
@@ -372,10 +367,10 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        doReturn(app).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(app).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
-        doReturn(null).when(sService).getTopApp();
+        doReturn(null).when(mService).getTopApp();
 
         assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ,
                 SCHED_GROUP_TOP_APP);
@@ -386,11 +381,11 @@
     public void testUpdateOomAdj_DoOne_TopApp_Awake() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(app).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(app).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
-        doReturn(null).when(sService).getTopApp();
+        doReturn(null).when(mService).getTopApp();
 
         assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_TOP_APP);
     }
@@ -400,11 +395,11 @@
     public void testUpdateOomAdj_DoOne_RunningAnimations() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState();
+        doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState();
         app.mState.setRunningRemoteAnimation(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
 
         assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
     }
@@ -415,7 +410,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         doReturn(mock(ActiveInstrumentation.class)).when(app).getActiveInstrumentation();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doCallRealMethod().when(app).getActiveInstrumentation();
 
@@ -429,11 +424,11 @@
     public void testUpdateOomAdj_DoOne_ReceivingBroadcast() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class),
+        doReturn(true).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class),
                 any(int[].class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
-        doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class),
+        doReturn(false).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class),
                 any(int[].class));
 
         assertProcStates(app, PROCESS_STATE_RECEIVER, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -445,7 +440,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mServices.startExecutingService(mock(ServiceRecord.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_SERVICE, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -456,13 +451,13 @@
     public void testUpdateOomAdj_DoOne_TopApp_Sleeping() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(app).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+        doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(app).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
         updateOomAdj(app);
-        doReturn(null).when(sService).getTopApp();
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(null).when(mService).getTopApp();
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ,
                 SCHED_GROUP_BACKGROUND);
@@ -475,8 +470,8 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ);
         app.mState.setCurAdj(CACHED_APP_MIN_ADJ);
-        doReturn(null).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(null).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         final int expectedAdj = sFirstCachedAdj;
@@ -505,7 +500,7 @@
             return 0;
         })).when(wpc).computeOomAdjFromActivities(
                 any(WindowProcessController.ComputeOomAdjCallback.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
@@ -522,7 +517,7 @@
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).hasRecentTasks();
         app.mState.setLastTopTime(SystemClock.uptimeMillis());
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doCallRealMethod().when(wpc).hasRecentTasks();
 
@@ -536,7 +531,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mServices.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION,
                 /* hasNoneType=*/false);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -550,7 +545,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -561,10 +556,10 @@
     @SuppressWarnings("GuardedBy")
     @Test
     public void testUpdateOomAdj_DoOne_FgService_ShortFgs() {
-        sService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000;
-        sService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000;
+        mService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000;
+        mService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000;
 
-        ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+        ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
@@ -579,7 +574,7 @@
             app.mServices.setHasForegroundServices(true,
                     FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
             app.mState.setLastTopTime(SystemClock.uptimeMillis());
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
             updateOomAdj(app);
 
@@ -596,8 +591,8 @@
                     FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
             app.mServices.startService(s);
             app.mState.setLastTopTime(SystemClock.uptimeMillis()
-                    - sService.mConstants.TOP_TO_FGS_GRACE_DURATION);
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+                    - mService.mConstants.TOP_TO_FGS_GRACE_DURATION);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
             updateOomAdj(app);
 
@@ -607,14 +602,14 @@
         }
 
         // SHORT_SERVICE, timed out already.
-        s = ServiceRecord.newEmptyInstanceForTest(sService);
+        s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
         s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
         s.setShortFgsInfo(SystemClock.uptimeMillis()
-                - sService.mConstants.mShortFgsTimeoutDuration
-                - sService.mConstants.mShortFgsProcStateExtraWaitDuration);
+                - mService.mConstants.mShortFgsTimeoutDuration
+                - mService.mConstants.mShortFgsProcStateExtraWaitDuration);
         {
             ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                     MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
@@ -622,8 +617,8 @@
                     FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false);
             app.mServices.startService(s);
             app.mState.setLastTopTime(SystemClock.uptimeMillis()
-                    - sService.mConstants.TOP_TO_FGS_GRACE_DURATION);
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+                    - mService.mConstants.TOP_TO_FGS_GRACE_DURATION);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
             updateOomAdj(app);
 
@@ -639,7 +634,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mState.setHasOverlayUi(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ,
@@ -653,12 +648,26 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         app.mState.setLastTopTime(SystemClock.uptimeMillis());
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE,
-                PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
+                PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT, "fg-service-act");
         assertBfsl(app);
+
+        if (!Flags.followUpOomadjUpdates()) return;
+
+        final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+        verify(mService.mHandler).sendEmptyMessageAtTime(
+                eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+        mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+        assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                SCHED_GROUP_DEFAULT, "fg-service");
+        // Follow up should not have been called again.
+        verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+                followUpTimeCaptor.capture());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -678,12 +687,24 @@
             s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime;
             s.getConnections().clear();
             app.mServices.updateHasTopStartedAlmostPerceptibleServices();
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(app);
 
             assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
 
-            sService.mOomAdjuster.resetInternal();
+            if (!Flags.followUpOomadjUpdates()) return;
+
+            final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+            verify(mService.mHandler).sendEmptyMessageAtTime(
+                    eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+            mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+            mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+            assertEquals(sFirstCachedAdj, app.mState.getSetAdj());
+            // Follow up should not have been called again.
+            verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+                    followUpTimeCaptor.capture());
+
         }
 
         // Out of grace period but valid binding allows the adjustment.
@@ -698,14 +719,14 @@
             ServiceRecord s = bindService(app, system,
                     null, null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class));
             s.lastTopAlmostPerceptibleBindRequestUptimeMs =
-                    nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
+                    nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
             app.mServices.updateHasTopStartedAlmostPerceptibleServices();
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(app);
 
             assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
 
-            sService.mOomAdjuster.resetInternal();
+            mService.mOomAdjuster.resetInternal();
         }
 
         // Out of grace period and no valid binding so no adjustment.
@@ -720,15 +741,15 @@
             ServiceRecord s = bindService(app, system,
                     null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
             s.lastTopAlmostPerceptibleBindRequestUptimeMs =
-                    nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
+                    nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs;
             s.getConnections().clear();
             app.mServices.updateHasTopStartedAlmostPerceptibleServices();
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(app);
 
             assertNotEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj());
 
-            sService.mOomAdjuster.resetInternal();
+            mService.mOomAdjuster.resetInternal();
         }
     }
 
@@ -744,7 +765,7 @@
         // Simulate the system starting and binding to a service in the app.
         ServiceRecord s = bindService(app, system,
                 null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(system, app);
 
         assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND,
@@ -757,7 +778,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mState.setForcingToImportant(new Object());
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -771,7 +792,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isHeavyWeightProcess();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
         doReturn(false).when(wpc).isHeavyWeightProcess();
 
@@ -786,7 +807,7 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isHomeProcess();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_HOME, HOME_APP_ADJ, SCHED_GROUP_BACKGROUND);
@@ -800,11 +821,25 @@
         WindowProcessController wpc = app.getWindowProcessController();
         doReturn(true).when(wpc).isPreviousProcess();
         doReturn(true).when(wpc).hasActivities();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
-                SCHED_GROUP_BACKGROUND);
+                SCHED_GROUP_BACKGROUND, "previous");
+
+        if (!Flags.followUpOomadjUpdates()) return;
+
+        final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+        verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+                followUpTimeCaptor.capture());
+        mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+        assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, CACHED_APP_MIN_ADJ,
+                SCHED_GROUP_BACKGROUND, "previous-expired");
+        // Follow up should not have been called again.
+        verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+                followUpTimeCaptor.capture());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -814,10 +849,10 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
         backupTarget.app = app;
-        doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
-        doReturn(null).when(sService.mBackupTargets).get(anyInt());
+        doReturn(null).when(mService.mBackupTargets).get(anyInt());
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, BACKUP_APP_ADJ,
                 SCHED_GROUP_BACKGROUND);
@@ -829,7 +864,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mServices.setHasClientActivities(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.mState.getSetProcState());
@@ -841,7 +876,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
         app.mServices.setTreatLikeActivity(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
@@ -858,7 +893,7 @@
         s.startRequested = true;
         s.lastActivity = SystemClock.uptimeMillis();
         app.mServices.startService(s);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_B_ADJ, SCHED_GROUP_BACKGROUND);
@@ -870,7 +905,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         app.mState.setMaxAdj(PERCEPTIBLE_LOW_APP_ADJ);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, PERCEPTIBLE_LOW_APP_ADJ,
@@ -884,8 +919,8 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         app.mState.setCurRawAdj(SERVICE_ADJ);
         app.mState.setCurAdj(SERVICE_ADJ);
-        doReturn(null).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(null).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.mState.getSetAdj());
@@ -902,7 +937,7 @@
         s.startRequested = true;
         s.lastActivity = SystemClock.uptimeMillis();
         app.mServices.startService(s);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
@@ -918,11 +953,11 @@
         ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY,
                 mock(IBinder.class));
         s.startRequested = true;
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(client).when(sService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(client).when(mService).getTopApp();
         updateOomAdj(client, app);
-        doReturn(null).when(sService).getTopApp();
+        doReturn(null).when(mService).getTopApp();
 
         assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
     }
@@ -937,7 +972,7 @@
         client.mServices.setTreatLikeActivity(true);
         bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY
                 | Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState());
@@ -956,9 +991,9 @@
         ConnectionRecord cr = s.getConnections().get(binder).get(0);
         setFieldValue(ConnectionRecord.class, cr, "activity",
                 mock(ActivityServiceConnectionsHolder.class));
-        doReturn(client).when(sService).getTopApp();
+        doReturn(client).when(mService).getTopApp();
         doReturn(true).when(cr.activity).isActivityVisible();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -971,7 +1006,7 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         bindService(app, app, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
@@ -986,7 +1021,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         client.mServices.setTreatLikeActivity(true);
         bindService(app, client, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState());
@@ -1005,11 +1040,11 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_ALLOW_OOM_MANAGEMENT,
                 mock(IBinder.class));
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(client).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(client).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
-        doReturn(null).when(sService).getTopApp();
+        doReturn(null).when(mService).getTopApp();
 
         assertEquals(PREVIOUS_APP_ADJ, app.mState.getSetAdj());
     }
@@ -1024,7 +1059,7 @@
         bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
         client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         client.mState.setHasTopUi(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -1041,7 +1076,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_IMPORTANT, mock(IBinder.class));
         client.mServices.startExecutingService(mock(ServiceRecord.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -1056,11 +1091,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, 0, mock(IBinder.class));
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(client).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(client).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
-        doReturn(null).when(sService).getTopApp();
+        doReturn(null).when(mService).getTopApp();
 
         assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT);
     }
@@ -1074,7 +1109,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class));
         client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.mState.getSetProcState());
@@ -1092,7 +1127,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class));
         client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState());
@@ -1108,7 +1143,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, 0, mock(IBinder.class));
         client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, client.mState.getSetProcState());
@@ -1128,7 +1163,7 @@
         bindService(app, client, null, null, 0, mock(IBinder.class));
 
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
-        ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+        ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
@@ -1139,7 +1174,7 @@
 
         client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
                 /* hasNoneType=*/false);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1159,7 +1194,7 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
 
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
-        ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+        ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
@@ -1170,7 +1205,7 @@
 
         app2.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
                 /* hasNoneType=*/false);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app2);
 
         // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1211,11 +1246,11 @@
         bindService(app, client, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class));
         BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0);
         backupTarget.app = client;
-        doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt());
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt());
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
-        doReturn(null).when(sService.mBackupTargets).get(anyInt());
+        doReturn(null).when(mService.mBackupTargets).get(anyInt());
 
         assertEquals(BACKUP_APP_ADJ, app.mState.getSetAdj());
         assertNoBfsl(app);
@@ -1236,7 +1271,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
         client.mState.setRunningRemoteAnimation(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj());
@@ -1251,7 +1286,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class));
         client.mState.setRunningRemoteAnimation(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1266,7 +1301,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, 0, mock(IBinder.class));
         client.mState.setHasOverlayUi(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1284,12 +1319,12 @@
                     Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND,
                     mock(IBinder.class));
             client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(client, app);
 
             assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj());
 
-            sService.mOomAdjuster.resetInternal();
+            mService.mOomAdjuster.resetInternal();
         }
 
         {
@@ -1303,13 +1338,13 @@
                     Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND,
                     mock(IBinder.class));
             client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(client, app);
             doReturn(false).when(wpc).isHeavyWeightProcess();
 
             assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj());
 
-            sService.mOomAdjuster.resetInternal();
+            mService.mOomAdjuster.resetInternal();
         }
 
         {
@@ -1321,12 +1356,12 @@
                     Context.BIND_ALMOST_PERCEPTIBLE,
                     mock(IBinder.class));
             client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(client, app);
 
             assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj());
 
-            sService.mOomAdjuster.resetInternal();
+            mService.mOomAdjuster.resetInternal();
         }
 
         {
@@ -1340,13 +1375,13 @@
                     Context.BIND_ALMOST_PERCEPTIBLE,
                     mock(IBinder.class));
             client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
-            sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+            mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
             updateOomAdj(client, app);
             doReturn(false).when(wpc).isHeavyWeightProcess();
 
             assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj());
 
-            sService.mOomAdjuster.resetInternal();
+            mService.mOomAdjuster.resetInternal();
         }
     }
 
@@ -1359,7 +1394,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindService(app, client, null, null, 0, mock(IBinder.class));
         client.mState.setRunningRemoteAnimation(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj());
@@ -1375,7 +1410,7 @@
         bindService(app, client, null, null, Context.BIND_IMPORTANT_BACKGROUND,
                 mock(IBinder.class));
         client.mState.setHasOverlayUi(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.mState.getSetProcState());
@@ -1402,7 +1437,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindProvider(app, client, null, null, false);
         client.mServices.setTreatLikeActivity(true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client);
 
         assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
@@ -1416,11 +1451,11 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindProvider(app, client, null, null, false);
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(client).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(client).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
-        doReturn(null).when(sService).getTopApp();
+        doReturn(null).when(mService).getTopApp();
 
         assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT);
     }
@@ -1434,7 +1469,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindProvider(app, client, null, null, false);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1452,7 +1487,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
 
         // In order to trick OomAdjuster to think it has a short-service, we need this logic.
-        ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService);
+        ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService);
         s.appInfo = new ApplicationInfo();
         s.startRequested = true;
         s.isForeground = true;
@@ -1464,7 +1499,7 @@
         client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
                 /* hasNoneType=*/false);
         bindProvider(app, client, null, null, false);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated.
@@ -1486,7 +1521,7 @@
         ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         bindProvider(app, client, null, null, true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, app);
 
         assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, FOREGROUND_APP_ADJ,
@@ -1499,11 +1534,25 @@
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         app.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
-                SCHED_GROUP_BACKGROUND);
+                SCHED_GROUP_BACKGROUND, "recent-provider");
+
+        if (!Flags.followUpOomadjUpdates()) return;
+
+        final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+        verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+                followUpTimeCaptor.capture());
+        mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+                "cch-empty");
+        // Follow up should not have been called again.
+        verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+                followUpTimeCaptor.capture());
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1517,11 +1566,11 @@
         ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, null, 0, mock(IBinder.class));
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(client2).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(client2).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
-        doReturn(null).when(sService).getTopApp();
+        doReturn(null).when(mService).getTopApp();
 
         assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT);
@@ -1539,7 +1588,7 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(app, client2, null, null, 0, mock(IBinder.class));
         client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1559,7 +1608,7 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client, client2, null, null, 0, mock(IBinder.class));
         client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1584,7 +1633,7 @@
         // Note: We add processes to LRU but still call updateOomAdjLocked() with a specific
         // processes.
         setProcessesToLru(app, client, client2);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1598,7 +1647,7 @@
         assertBfsl(client2);
 
         client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client2);
 
         assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState());
@@ -1622,7 +1671,7 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindService(client2, client, null, null, 0, mock(IBinder.class));
         client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1649,7 +1698,7 @@
         bindService(client, client2, null, null, 0, mock(IBinder.class));
         bindService(client2, client, null, null, 0, mock(IBinder.class));
         client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1683,7 +1732,7 @@
         bindService(client3, client4, null, null, 0, mock(IBinder.class));
         bindService(client4, client3, null, null, 0, mock(IBinder.class));
         client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3, client4);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1720,7 +1769,7 @@
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
         client3.mState.setForcingToImportant(new Object());
         bindService(app, client3, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1746,7 +1795,7 @@
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
         client3.mState.setForcingToImportant(new Object());
         bindService(app, client3, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3);
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -1773,7 +1822,7 @@
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
         client4.mState.setForcingToImportant(new Object());
         bindService(app, client4, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3, client4);
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -1802,7 +1851,7 @@
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
         client4.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindService(app, client4, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3, client4);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1828,7 +1877,7 @@
                 MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
         client3.mState.setForcingToImportant(new Object());
         bindService(app, client3, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, client3, app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1848,7 +1897,7 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindProvider(client, client2, null, null, false);
         client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1869,7 +1918,7 @@
         bindProvider(client, client2, null, null, false);
         client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindService(client2, app, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1889,7 +1938,7 @@
                 MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         bindProvider(client, client2, null, null, false);
         client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client, client2, app);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1910,7 +1959,7 @@
         bindProvider(client, client2, null, null, false);
         client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindProvider(client2, app, null, null, false);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -1936,7 +1985,7 @@
         client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
 
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(client1, client2, app1, app2);
 
         assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
@@ -1957,7 +2006,7 @@
         assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
                 SCHED_GROUP_DEFAULT);
 
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
         updateOomAdj(client1, client2, app1, app2);
         assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ,
                 SCHED_GROUP_TOP_APP);
@@ -2028,9 +2077,9 @@
                 SCHED_GROUP_DEFAULT);
 
         client2.mState.setHasOverlayUi(false);
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(client2).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(client2).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         updateOomAdj(client2, app2);
         assertProcStates(app2, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ,
@@ -2047,7 +2096,7 @@
         client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
 
         app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         bindService(app1, client1, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class));
 
@@ -2068,7 +2117,7 @@
         client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
 
         app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         bindService(app1, client1, null, null, Context.BIND_ALMOST_PERCEPTIBLE,
                 mock(IBinder.class));
@@ -2088,7 +2137,7 @@
         app.setPendingFinishAttach(true);
         app.mState.setHasForegroundActivities(false);
 
-        sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
+        mService.mOomAdjuster.setAttachingProcessStatesLSP(app);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ,
@@ -2102,9 +2151,9 @@
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
         app.setPendingFinishAttach(true);
         app.mState.setHasForegroundActivities(true);
-        doReturn(app).when(sService).getTopApp();
+        doReturn(app).when(mService).getTopApp();
 
-        sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
+        mService.mOomAdjuster.setAttachingProcessStatesLSP(app);
         updateOomAdj(app);
 
         assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ,
@@ -2124,10 +2173,10 @@
                 MOCKAPP4_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
         final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID,
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
-        final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, sService);
-        final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, sService);
-        final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, sService);
-        final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, sService);
+        final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, mService);
+        final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, mService);
+        final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, mService);
+        final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, mService);
         app1.setUidRecord(app1UidRecord);
         app2.setUidRecord(app2UidRecord);
         app3.setUidRecord(app3UidRecord);
@@ -2137,7 +2186,7 @@
         client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         client2.mState.setForcingToImportant(new Object());
         setProcessesToLru(app1, app2, app3, client1, client2);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
 
         final ComponentName cn1 = ComponentName.unflattenFromString(
                 MOCKAPP_PACKAGENAME + "/.TestService");
@@ -2164,10 +2213,10 @@
         c2s.startRequested = true;
 
         try {
-            sService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord);
-            sService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord);
-            sService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord);
-            sService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord);
+            mService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord);
+            mService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord);
+            mService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord);
+            mService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord);
 
             setServiceMap(s1, MOCKAPP_UID, cn1);
             setServiceMap(s2, MOCKAPP2_UID, cn2);
@@ -2195,10 +2244,10 @@
             app2UidRecord.setIdle(true);
             app3UidRecord.setIdle(true);
             clientUidRecord.setIdle(true);
-            doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService)
+            doReturn(ActivityManager.APP_START_MODE_DELAYED).when(mService)
                     .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
                             anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
-            doNothing().when(sService.mServices)
+            doNothing().when(mService.mServices)
                     .scheduleServiceTimeoutLocked(any(ProcessRecord.class));
             updateOomAdj(client1, client2, app1, app2, app3);
 
@@ -2206,11 +2255,11 @@
             assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState());
             assertEquals(PROCESS_STATE_SERVICE, client2.mState.getSetProcState());
         } finally {
-            doCallRealMethod().when(sService)
+            doCallRealMethod().when(mService)
                     .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(),
                             anyInt(), anyBoolean(), anyBoolean(), anyBoolean());
-            sService.mServices.mServiceMap.clear();
-            sService.mOomAdjuster.mActiveUids.clear();
+            mService.mServices.mServiceMap.clear();
+            mService.mOomAdjuster.mActiveUids.clear();
         }
     }
 
@@ -2224,7 +2273,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
 
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2);
 
         assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ,
@@ -2244,7 +2293,7 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindService(app, app2, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2268,7 +2317,7 @@
         bindService(app2, app3, null, null, 0, mock(IBinder.class));
         app3.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindService(app3, app, null, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2, app3);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2313,7 +2362,7 @@
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
         app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindService(app, app5, null, s, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2, app3, app4, app5);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2355,7 +2404,7 @@
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
         app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindService(app, app5, null, s, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app5, app4, app3, app2, app);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2397,7 +2446,7 @@
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
         app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindService(app, app5, null, s, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app3, app4, app2, app, app5);
 
         assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2437,7 +2486,7 @@
         client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         bindService(app, client3, null, null, Context.BIND_INCLUDE_CAPABILITIES,
                 mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client, client2, client3);
 
         final int expected = PROCESS_CAPABILITY_ALL & ~PROCESS_CAPABILITY_BFSL;
@@ -2468,7 +2517,7 @@
                 MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false));
         app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true);
         bindProvider(app, app5, cr, null, false);
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, app2, app3, app4, app5);
 
         assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
@@ -2512,8 +2561,8 @@
         doCallRealMethod().when(s).getConnections();
         s.startRequested = true;
         s.lastActivity = now;
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
-        sService.mOomAdjuster.mNumServiceProcs = 3;
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mOomAdjuster.mNumServiceProcs = 3;
         updateOomAdj(app3, app2, app);
 
         assertEquals(SERVICE_B_ADJ, app3.mState.getSetAdj());
@@ -2530,15 +2579,15 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         final int userOwner = 0;
         final int userOther = 1;
-        final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ
+        final int cachedAdj1 = mService.mConstants.USE_TIERED_CACHED_ADJ
                 ? CACHED_APP_MIN_ADJ + 10
                 : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ
+        final int cachedAdj2 = mService.mConstants.USE_TIERED_CACHED_ADJ
                 ? CACHED_APP_MIN_ADJ + 10
                 : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
-        doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
+        doReturn(userOwner).when(mService.mUserController).getCurrentUserId();
 
-        final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
+        final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP();
         lru.clear();
         lru.add(app2);
         lru.add(app);
@@ -2549,10 +2598,10 @@
                 MOCKAPP2_PACKAGENAME + "/.TestService");
         final long now = SystemClock.uptimeMillis();
 
-        sService.mConstants.KEEP_WARMING_SERVICES.clear();
+        mService.mConstants.KEEP_WARMING_SERVICES.clear();
         final ServiceInfo si = mock(ServiceInfo.class);
         si.applicationInfo = mock(ApplicationInfo.class);
-        ServiceRecord s = spy(new ServiceRecord(sService, cn, cn, null, 0, null,
+        ServiceRecord s = spy(new ServiceRecord(mService, cn, cn, null, 0, null,
                 si, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
         s.startRequested = true;
@@ -2564,16 +2613,16 @@
         final ServiceInfo si2 = mock(ServiceInfo.class);
         si2.applicationInfo = mock(ApplicationInfo.class);
         si2.applicationInfo.uid = MOCKAPP2_UID_OTHER;
-        ServiceRecord s2 = spy(new ServiceRecord(sService, cn2, cn2, null, 0, null,
+        ServiceRecord s2 = spy(new ServiceRecord(mService, cn2, cn2, null, 0, null,
                 si2, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections();
         s2.startRequested = true;
-        s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+        s2.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
 
         app2.mServices.startService(s2);
         app2.mState.setHasShownUi(false);
 
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
 
         assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
@@ -2590,7 +2639,7 @@
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
         app.mState.setAdjType(null);
         app.mState.setSetAdj(UNKNOWN_ADJ);
-        s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+        s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
         updateOomAdj();
 
         assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
@@ -2600,9 +2649,9 @@
         app.mState.setAdjType(null);
         app.mState.setSetAdj(UNKNOWN_ADJ);
         app.mState.setHasShownUi(true);
-        sService.mConstants.KEEP_WARMING_SERVICES.add(cn);
-        sService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
-        s = spy(new ServiceRecord(sService, cn, cn, null, 0, null,
+        mService.mConstants.KEEP_WARMING_SERVICES.add(cn);
+        mService.mConstants.KEEP_WARMING_SERVICES.add(cn2);
+        s = spy(new ServiceRecord(mService, cn, cn, null, 0, null,
                 si, false, null));
         doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections();
         s.startRequested = true;
@@ -2618,14 +2667,14 @@
         app.mState.setAdjType(null);
         app.mState.setSetAdj(UNKNOWN_ADJ);
         app.mState.setHasShownUi(false);
-        s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1;
+        s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
         updateOomAdj();
 
         assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
         assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
 
-        doReturn(userOther).when(sService.mUserController).getCurrentUserId();
-        sService.mOomAdjuster.handleUserSwitchedLocked();
+        doReturn(userOther).when(mService.mUserController).getCurrentUserId();
+        mService.mOomAdjuster.handleUserSwitchedLocked();
 
         updateOomAdj();
         assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
@@ -2637,9 +2686,9 @@
     public void testUpdateOomAdj_DoOne_AboveClient_SameProcess() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(app).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(app).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -2672,8 +2721,8 @@
         s = bindService(app3, app2, null, null, 0, mock(IBinder.class));
         s.lastActivity = now;
 
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
-        sService.mOomAdjuster.mNumServiceProcs = 3;
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mOomAdjuster.mNumServiceProcs = 3;
         updateOomAdj(app, app2, app3);
 
         assertEquals(SERVICE_ADJ, app.mState.getSetAdj());
@@ -2688,9 +2737,9 @@
     public void testUpdateOomAdj_DoOne_AboveClient_NotStarted() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
-        doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState();
-        doReturn(app).when(sService).getTopApp();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState();
+        doReturn(app).when(mService).getTopApp();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
         assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj());
@@ -2718,7 +2767,7 @@
         ServiceRecord s = makeServiceRecord(app);
         s.startRequested = true;
         s.lastActivity = SystemClock.uptimeMillis();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
 
@@ -2738,7 +2787,7 @@
         ServiceRecord s = makeServiceRecord(app);
         s.startRequested = true;
         s.lastActivity = SystemClock.uptimeMillis();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdjPending(app);
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
 
@@ -2760,7 +2809,7 @@
         ServiceRecord s = makeServiceRecord(app);
         s.startRequested = true;
         s.lastActivity = SystemClock.uptimeMillis();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
 
@@ -2781,7 +2830,7 @@
         ServiceRecord s = makeServiceRecord(app);
         s.startRequested = true;
         s.lastActivity = SystemClock.uptimeMillis();
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdjPending(app);
         assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND);
 
@@ -2809,7 +2858,7 @@
         client.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
         attributedClient.mServices.setHasForegroundServices(true, 0, true);
         bindService(sandboxService, client, attributedClient, null, 0, mock(IBinder.class));
-        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
         assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ,
                 SCHED_GROUP_DEFAULT);
@@ -2830,13 +2879,13 @@
 
         // App1 binds to app2 and gets temp allowlisted.
         bindService(app2, app, null, null, 0, mock(IBinder.class));
-        sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
+        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
 
         assertEquals(true, app.getUidRecord().isSetAllowListed());
         assertEquals(true, app.mOptRecord.shouldNotFreeze());
         assertEquals(true, app2.mOptRecord.shouldNotFreeze());
 
-        sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
+        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertEquals(false, app.mOptRecord.shouldNotFreeze());
         assertEquals(false, app2.mOptRecord.shouldNotFreeze());
@@ -2856,8 +2905,8 @@
         // App1 and app2 both bind to app3 and get temp allowlisted.
         bindService(app3, app, null, null, 0, mock(IBinder.class));
         bindService(app3, app2, null, null, 0, mock(IBinder.class));
-        sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
-        sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true);
+        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true);
+        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true);
 
         assertEquals(true, app.getUidRecord().isSetAllowListed());
         assertEquals(true, app2.getUidRecord().isSetAllowListed());
@@ -2866,7 +2915,7 @@
         assertEquals(true, app3.mOptRecord.shouldNotFreeze());
 
         // Remove app1 from allowlist.
-        sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
+        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertEquals(true, app2.getUidRecord().isSetAllowListed());
         assertEquals(false, app.mOptRecord.shouldNotFreeze());
@@ -2874,7 +2923,7 @@
         assertEquals(true, app3.mOptRecord.shouldNotFreeze());
 
         // Now remove app2 from allowlist.
-        sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
+        mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false);
         assertEquals(false, app.getUidRecord().isSetAllowListed());
         assertEquals(false, app2.getUidRecord().isSetAllowListed());
         assertEquals(false, app.mOptRecord.shouldNotFreeze());
@@ -2882,6 +2931,73 @@
         assertEquals(false, app3.mOptRecord.shouldNotFreeze());
     }
 
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoAll_ClientlessService() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+
+        setProcessesToLru(app);
+        ServiceRecord s = makeServiceRecord(app);
+        s.startRequested = true;
+        s.lastActivity = SystemClock.uptimeMillis();
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        updateOomAdj();
+        assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+                "started-services");
+
+        if (!Flags.followUpOomadjUpdates()) return;
+
+        final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+        verify(mService.mHandler).sendEmptyMessageAtTime(
+                eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+        mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+        assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+                "cch-started-services");
+        // Follow up should not have been called again.
+        verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
+                followUpTimeCaptor.capture());
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoAll_Multiple_Provider_Retention() {
+        ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+        ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+        app1.mProviders.setLastProviderTime(SystemClock.uptimeMillis());
+        app2.mProviders.setLastProviderTime(SystemClock.uptimeMillis() + 2000);
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setProcessesToLru(app1, app2);
+        mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+
+        assertProcStates(app1, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+                SCHED_GROUP_BACKGROUND, "recent-provider");
+        assertProcStates(app2, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+                SCHED_GROUP_BACKGROUND, "recent-provider");
+
+        if (!Flags.followUpOomadjUpdates()) return;
+
+        final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class);
+        verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
+                eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+        mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+
+        assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+                "cch-empty");
+
+        verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
+                eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
+        mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
+        mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
+        assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+                "cch-empty");
+    }
+
     private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
             String packageName, boolean hasShownUi) {
         return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi(
@@ -2904,7 +3020,7 @@
     }
 
     private void setServiceMap(ServiceRecord s, int uid, ComponentName cn) {
-        ActiveServices.ServiceMap serviceMap = sService.mServices.mServiceMap.get(
+        ActiveServices.ServiceMap serviceMap = mService.mServices.mServiceMap.get(
                 UserHandle.getUserId(uid));
         if (serviceMap == null) {
             serviceMap = mock(ActiveServices.ServiceMap.class);
@@ -2916,7 +3032,7 @@
                     new ArrayMap<>());
             setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mDelayedStartList",
                     new ArrayList<>());
-            sService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap);
+            mService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap);
         }
         serviceMap.mServicesByInstanceName.put(cn, s);
     }
@@ -2957,6 +3073,7 @@
         return record;
     }
 
+    @SuppressWarnings("GuardedBy")
     private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
             int expectedSchedGroup) {
         final ProcessStateRecord state = app.mState;
@@ -2974,6 +3091,7 @@
         }
     }
 
+    @SuppressWarnings("GuardedBy")
     private void assertProcStates(ProcessRecord app, boolean expectedCached,
             int expectedProcState, int expectedAdj, String expectedAdjType) {
         final ProcessStateRecord state = app.mState;
@@ -2992,7 +3110,26 @@
         }
     }
 
-    private static class ProcessRecordBuilder {
+    @SuppressWarnings("GuardedBy")
+    private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
+            int expectedSchedGroup, String expectedAdjType) {
+        final ProcessStateRecord state = app.mState;
+        assertEquals(expectedAdjType, state.getAdjType());
+        assertEquals(expectedProcState, state.getSetProcState());
+        assertEquals(expectedAdj, state.getSetAdj());
+        assertEquals(expectedSchedGroup, state.getSetSchedGroup());
+
+        // Below BFGS should never have BFSL.
+        if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+            assertNoBfsl(app);
+        }
+        // Above FGS should always have BFSL.
+        if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) {
+            assertBfsl(app);
+        }
+    }
+
+    private class ProcessRecordBuilder {
         @SuppressWarnings("UnusedVariable")
         int mPid;
         int mUid;
@@ -3069,17 +3206,17 @@
             ai.packageName = mPackageName;
             ai.longVersionCode = mVersionCode;
             ai.targetSdkVersion = mTargetSdkVersion;
-            doCallRealMethod().when(sService).getPackageManagerInternal();
-            doReturn(null).when(sPackageManagerInternal).getApplicationInfo(
+            doCallRealMethod().when(mService).getPackageManagerInternal();
+            doReturn(null).when(mPackageManagerInternal).getApplicationInfo(
                     eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt());
-            ProcessRecord app = new ProcessRecord(sService, ai, mProcessName, mUid,
+            ProcessRecord app = new ProcessRecord(mService, ai, mProcessName, mUid,
                     mSdkSandboxClientAppPackage, -1, null);
             final ProcessStateRecord state = app.mState;
             final ProcessServiceRecord services = app.mServices;
             final ProcessReceiverRecord receivers = app.mReceivers;
             final ProcessProfileRecord profile = app.mProfile;
             final ProcessProviderRecord providers = app.mProviders;
-            app.makeActive(mock(IApplicationThread.class), sService.mProcessStats);
+            app.makeActive(mock(IApplicationThread.class), mService.mProcessStats);
             app.setLastActivityTime(mLastActivityTime);
             app.setKilledByAm(mKilledByAm);
             app.setIsolatedEntryPoint(mIsolatedEntryPoint);
@@ -3124,14 +3261,35 @@
             }
             providers.setLastProviderTime(mLastProviderTime);
 
-            UidRecord uidRec = sService.mOomAdjuster.mActiveUids.get(mUid);
+            UidRecord uidRec = mService.mOomAdjuster.mActiveUids.get(mUid);
             if (uidRec == null) {
-                uidRec = new UidRecord(mUid, sService);
-                sService.mOomAdjuster.mActiveUids.put(mUid, uidRec);
+                uidRec = new UidRecord(mUid, mService);
+                mService.mOomAdjuster.mActiveUids.put(mUid, uidRec);
             }
             uidRec.addProcess(app);
             app.setUidRecord(uidRec);
             return app;
         }
     }
+
+    static class OomAdjusterInjector extends OomAdjuster.Injector {
+        // Jump ahead in time by this offset amount.
+        long mTimeOffsetMillis = 0;
+
+        void jumpUptimeAheadTo(long uptimeMillis) {
+            final long jumpMs = uptimeMillis - getUptimeMillis();
+            if (jumpMs <= 0) return;
+            mTimeOffsetMillis += jumpMs;
+        }
+
+        @Override
+        long getUptimeMillis() {
+            return SystemClock.uptimeMillis() + mTimeOffsetMillis;
+        }
+
+        @Override
+        long getElapsedRealtimeMillis() {
+            return SystemClock.elapsedRealtime() + mTimeOffsetMillis;
+        }
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
index 1b0a8d2..f1bf86f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperCropperTest.java
@@ -211,9 +211,11 @@
                     new Rect(100, 200, bitmapSize.x - 100, bitmapSize.y))) {
                 for (int mode: ALL_MODES) {
                     for (boolean parallax: List.of(true, false)) {
-                        assertThat(WallpaperCropper.getAdjustedCrop(
-                                crop, bitmapSize, displaySize, parallax, mode))
-                                .isEqualTo(crop);
+                        for (boolean rtl: List.of(true, false)) {
+                            assertThat(WallpaperCropper.getAdjustedCrop(
+                                    crop, bitmapSize, displaySize, parallax, rtl, mode))
+                                    .isEqualTo(crop);
+                        }
                     }
                 }
             }
@@ -234,8 +236,11 @@
         Point expectedCropSize = new Point(expectedWidth, 1000);
         for (int mode: ALL_MODES) {
             assertThat(WallpaperCropper.getAdjustedCrop(
-                    crop, bitmapSize, displaySize, true, mode))
-                    .isEqualTo(centerOf(crop, expectedCropSize));
+                    crop, bitmapSize, displaySize, true, false, mode))
+                    .isEqualTo(leftOf(crop, expectedCropSize));
+            assertThat(WallpaperCropper.getAdjustedCrop(
+                    crop, bitmapSize, displaySize, true, true, mode))
+                    .isEqualTo(rightOf(crop, expectedCropSize));
         }
     }
 
@@ -254,9 +259,11 @@
             Point bitmapSize = new Point(acceptableWidth, 1000);
             Rect crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y);
             for (int mode : ALL_MODES) {
-                assertThat(WallpaperCropper.getAdjustedCrop(
-                        crop, bitmapSize, displaySize, true, mode))
-                        .isEqualTo(crop);
+                for (boolean rtl : List.of(false, true)) {
+                    assertThat(WallpaperCropper.getAdjustedCrop(
+                            crop, bitmapSize, displaySize, true, rtl, mode))
+                            .isEqualTo(crop);
+                }
             }
         }
     }
@@ -286,9 +293,11 @@
         for (int i = 0; i < crops.size(); i++) {
             Rect crop = crops.get(i);
             Rect expectedCrop = expectedAdjustedCrops.get(i);
-            assertThat(WallpaperCropper.getAdjustedCrop(
-                    crop, bitmapSize, displaySize, false, WallpaperCropper.ADD))
-                    .isEqualTo(expectedCrop);
+            for (boolean rtl: List.of(false, true)) {
+                assertThat(WallpaperCropper.getAdjustedCrop(
+                        crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.ADD))
+                        .isEqualTo(expectedCrop);
+            }
         }
     }
 
@@ -309,9 +318,11 @@
         Point expectedCropSize = new Point(1000, 1000);
 
         for (Rect crop: crops) {
-            assertThat(WallpaperCropper.getAdjustedCrop(
-                    crop, bitmapSize, displaySize, false, WallpaperCropper.REMOVE))
-                    .isEqualTo(centerOf(crop, expectedCropSize));
+            for (boolean rtl : List.of(false, true)) {
+                assertThat(WallpaperCropper.getAdjustedCrop(
+                        crop, bitmapSize, displaySize, false, rtl, WallpaperCropper.REMOVE))
+                        .isEqualTo(centerOf(crop, expectedCropSize));
+            }
         }
     }
 
@@ -338,14 +349,14 @@
             Rect crop = crops.get(i);
             Rect expected = expectedAdjustedCrops.get(i);
             assertThat(WallpaperCropper.getAdjustedCrop(
-                    crop, bitmapSize, displaySize, false, WallpaperCropper.BALANCE))
+                    crop, bitmapSize, displaySize, false, false, WallpaperCropper.BALANCE))
                     .isEqualTo(expected);
 
             Rect transposedCrop = new Rect(crop.top, crop.left, crop.bottom, crop.right);
             Rect expectedTransposed = new Rect(
                     expected.top, expected.left, expected.bottom, expected.right);
             assertThat(WallpaperCropper.getAdjustedCrop(transposedCrop, bitmapSize,
-                    transposedDisplaySize, false, WallpaperCropper.BALANCE))
+                    transposedDisplaySize, false, false, WallpaperCropper.BALANCE))
                     .isEqualTo(expectedTransposed);
         }
     }
@@ -376,9 +387,11 @@
             Point displaySize = displaySizes.get(i);
             Point expectedCropSize = expectedCropSizes.get(i);
             for (boolean rtl : List.of(false, true)) {
+                Rect expectedCrop = rtl ? rightOf(bitmapRect, expectedCropSize)
+                        : leftOf(bitmapRect, expectedCropSize);
                 assertThat(mWallpaperCropper.getCrop(
                         displaySize, bitmapSize, suggestedCrops, rtl))
-                        .isEqualTo(centerOf(bitmapRect, expectedCropSize));
+                        .isEqualTo(expectedCrop);
             }
         }
     }
diff --git a/services/tests/ondeviceintelligencetests/Android.bp b/services/tests/ondeviceintelligencetests/Android.bp
new file mode 100644
index 0000000..aa85942
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/Android.bp
@@ -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 {
+    // 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_test {
+    name: "FrameworksOnDeviceIntelligenceTests",
+    team: "trendy_team_machine_learning",
+    defaults: [
+        "modules-utils-testable-device-config-defaults",
+    ],
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.ext.truth",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+        "services.core",
+        "servicestests-core-utils",
+        "servicestests-utils-mockito-extended",
+        "truth",
+        "frameworks-base-testutils",
+        "androidx.test.rules",
+    ],
+
+    libs: [
+        "android.test.mock",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests"],
+
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/ondeviceintelligencetests/AndroidManifest.xml b/services/tests/ondeviceintelligencetests/AndroidManifest.xml
new file mode 100644
index 0000000..8bd111e
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.frameworks.ondeviceintelligencetests">
+
+    <application android:testOnly="true"
+                 android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.frameworks.ondeviceintelligencetests"
+        android:label="Frameworks OnDeviceIntelligence Services Tests" />
+
+</manifest>
diff --git a/services/tests/ondeviceintelligencetests/AndroidTest.xml b/services/tests/ondeviceintelligencetests/AndroidTest.xml
new file mode 100644
index 0000000..3ae96c5
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/AndroidTest.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.
+-->
+<configuration description="Runs Frameworks OnDeviceIntelligence Services Tests.">
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="install-arg" value="-t" />
+        <option name="test-file-name" value="FrameworksOnDeviceIntelligenceTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="FrameworksOnDeviceIntelligenceTests" />
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.ondeviceintelligencetests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/services/tests/ondeviceintelligencetests/OWNERS b/services/tests/ondeviceintelligencetests/OWNERS
new file mode 100644
index 0000000..09774f7
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/ondeviceintelligence/OWNERS
diff --git a/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java b/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
new file mode 100644
index 0000000..d3943e3
--- /dev/null
+++ b/services/tests/ondeviceintelligencetests/src/com/android/server/ondeviceintelligence/InferenceInfoStoreTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
+import android.app.ondeviceintelligence.InferenceInfo;
+import android.util.Base64;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.framework.protobuf.nano.MessageNano;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class InferenceInfoStoreTest {
+    InferenceInfoStore inferenceInfoStore;
+
+    @Before
+    public void setUp() {
+        inferenceInfoStore = new InferenceInfoStore(1000);
+    }
+
+    @Test
+    public void testInferenceInfoParsesFromBundleSuccessfully() throws Exception {
+        Bundle bundle = new Bundle();
+        bundle.putByteArray(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+                getInferenceInfoBytes(1, 1, 100));
+        inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+        List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
+        assertThat(inferenceInfos).hasSize(1);
+        assertThat(inferenceInfos.get(0).getUid()).isEqualTo(1);
+        assertThat(inferenceInfos.get(0).getStartTimeMs()).isEqualTo(1);
+        assertThat(inferenceInfos.get(0).getEndTimeMs()).isEqualTo(100);
+    }
+
+    @Test
+    public void testInferenceInfoParsesFromPersistableBundleSuccessfully() throws Exception {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+                Base64.encodeToString(getInferenceInfoBytes(1, 1, 100), Base64.DEFAULT));
+        inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+        List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
+        assertThat(inferenceInfos).hasSize(1);
+        assertThat(inferenceInfos.get(0).getUid()).isEqualTo(1);
+        assertThat(inferenceInfos.get(0).getStartTimeMs()).isEqualTo(1);
+        assertThat(inferenceInfos.get(0).getEndTimeMs()).isEqualTo(100);
+    }
+
+
+    @Test
+    public void testEvictionAfterMaxAge() throws Exception {
+        PersistableBundle bundle = new PersistableBundle();
+        long testStartTime = System.currentTimeMillis();
+        bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+                Base64.encodeToString(getInferenceInfoBytes(1,  testStartTime - 10,
+                        testStartTime + 100), Base64.DEFAULT));
+        inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+        bundle.putString(OnDeviceSandboxedInferenceService.INFERENCE_INFO_BUNDLE_KEY,
+                Base64.encodeToString(getInferenceInfoBytes(1, testStartTime - 5,
+                        testStartTime + 100), Base64.DEFAULT));
+        inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+        Thread.sleep(1020);
+        List<InferenceInfo> inferenceInfos = inferenceInfoStore.getLatestInferenceInfo(0);
+        assertThat(inferenceInfos).hasSize(2);
+        assertThat(inferenceInfos.get(0).getUid()).isEqualTo(1);
+        assertThat(inferenceInfos.get(0).getStartTimeMs()).isEqualTo(testStartTime - 10);
+        assertThat(inferenceInfos.get(0).getEndTimeMs()).isEqualTo(testStartTime + 100);
+        inferenceInfoStore.addInferenceInfoFromBundle(bundle);
+        List<InferenceInfo> inferenceInfos2 = inferenceInfoStore.getLatestInferenceInfo(0);
+        assertThat(inferenceInfos2).hasSize(1); //previous entries should have been evicted
+    }
+
+    private byte[] getInferenceInfoBytes(int uid, long startTime, long endTime) {
+        com.android.server.ondeviceintelligence.nano.InferenceInfo inferenceInfo =
+                new com.android.server.ondeviceintelligence.nano.InferenceInfo();
+        inferenceInfo.uid = uid;
+        inferenceInfo.startTimeMs = startTime;
+        inferenceInfo.endTimeMs = endTime;
+        return MessageNano.toByteArray(inferenceInfo);
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 851cf4a..a3f0770 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -91,7 +91,7 @@
         final Parcel parcel = Parcel.obtain();
         parcel.writeParcelable(outBatteryUsageStats, 0);
 
-        assertThat(parcel.dataSize()).isLessThan(8000);
+        assertThat(parcel.dataSize()).isLessThan(12000);
 
         parcel.setDataPosition(0);
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
new file mode 100644
index 0000000..36deb08
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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 static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+
+public class CameraPowerStatsTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_CAMERA, 100.0)
+            .initMeasuredEnergyStatsLocked();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int VOLTAGE_MV = 3500;
+    private static final int ENERGY_CONSUMER_ID = 777;
+
+    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+
+    EnergyConsumerPowerStatsCollector.Injector mInjector =
+            new EnergyConsumerPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+
+                @Override
+                public IntSupplier getVoltageSupplier() {
+                    return () -> VOLTAGE_MV;
+                }
+            };
+
+    private MonotonicClock mMonotonicClock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+    }
+
+    @Test
+    public void energyConsumerModel() {
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CAMERA, null))
+                .thenReturn(new int[]{ENERGY_CONSUMER_ID});
+        CameraPowerStatsProcessor processor = new CameraPowerStatsProcessor(
+                mStatsRule.getPowerProfile(), mUidResolver);
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+        CameraPowerStatsCollector collector = new CameraPowerStatsCollector(mInjector);
+        collector.addConsumer(
+                powerStats -> {
+                    processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+                });
+        collector.setEnabled(true);
+
+        // Establish a baseline
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(new long[]{uCtoUj(10000)});
+        collector.collectAndDeliverStats();
+
+        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
+
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(new long[]{uCtoUj(2_170_000)});
+        collector.collectAndDeliverStats();
+
+        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
+
+        mStatsRule.setTime(11_000, 11_000);
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(new long[]{uCtoUj(3_610_000)});
+        collector.collectAndDeliverStats();
+
+        processor.finish(stats, 11_000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+        statsLayout.fromExtras(descriptor.extras);
+
+        // Total estimated power = 3,600,000 uC = 1.0 mAh
+        // of which 3,000,000 is distributed:
+        //     Screen-on  - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
+        //     Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
+        // and 600,000 was fully with screen off:
+        //     Screen-off - 1440000 uC = 0.4 mAh
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.25);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.35 + 0.4);
+
+        // UID1 =
+        //     2,160,000 uC = 0.6 mAh
+        //     split between three different states
+        //          fg screen-on: 2500/6000
+        //          bg screen-off: 2500/6000
+        //          fgs screen-off: 1000/6000
+        double expectedPower1 = 0.6;
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+        // UID2 =
+        //     1440000 mA-ms = 0.4 mAh
+        //     all in the same state
+        double expectedPower2 = 0.4;
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
+            int uid) {
+        mStatsRule.setTime(timestamp, timestamp);
+        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+        historyItem.time = mMonotonicClock.monotonicTime();
+        historyItem.states2 = stateOn ? BatteryStats.HistoryItem.STATE2_CAMERA_FLAG : 0;
+        if (stateOn) {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
+        } else {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+        }
+        historyItem.eventTag = historyItem.localEventTag;
+        historyItem.eventTag.uid = uid;
+        historyItem.eventTag.string = "camera";
+        return historyItem;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            BinaryStatePowerStatsProcessor processor) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(processor);
+
+        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
+        PowerComponentAggregatedPowerStats powerComponentStats =
+                aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_CAMERA);
+        processor.start(powerComponentStats, 0);
+
+        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        return powerComponentStats;
+    }
+
+    private static long uCtoUj(long uc) {
+        return (long) (uc * (double) VOLTAGE_MV / 1000);
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
new file mode 100644
index 0000000..8a391c6
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
@@ -0,0 +1,375 @@
+/*
+ * 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 static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.location.GnssSignalQuality;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.IntSupplier;
+
+public class GnssPowerStatsTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_GPS_ON, 100.0)
+            .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, new double[]{1000, 100})
+            .initMeasuredEnergyStatsLocked();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int VOLTAGE_MV = 3500;
+    private static final int ENERGY_CONSUMER_ID = 777;
+
+    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+
+    EnergyConsumerPowerStatsCollector.Injector mInjector =
+            new EnergyConsumerPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+
+                @Override
+                public IntSupplier getVoltageSupplier() {
+                    return () -> VOLTAGE_MV;
+                }
+            };
+
+    private MonotonicClock mMonotonicClock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+    }
+
+    @Test
+    public void powerProfileModel() {
+        // ODPM unsupported
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null))
+                .thenReturn(new int[0]);
+        GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor(
+                mStatsRule.getPowerProfile(), mUidResolver);
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+        GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
+        collector.addConsumer(
+                powerStats -> {
+                    processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+                });
+        collector.setEnabled(true);
+
+        // Establish a baseline
+        collector.collectAndDeliverStats();
+
+        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
+
+        collector.collectAndDeliverStats();
+
+        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
+        processor.noteStateChange(stats, buildHistoryItem(7000,
+                GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
+        processor.noteStateChange(stats, buildHistoryItem(8000,
+                GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
+        mStatsRule.setTime(11_000, 11_000);
+        collector.collectAndDeliverStats();
+
+        processor.finish(stats, 11_000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+        statsLayout.fromExtras(descriptor.extras);
+
+        // scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh
+        // scr-off GNSS=good: 4500 * 100 = 0.12500 mAh
+        // scr-off GNSS=poor: 3000 * 1000 = 0.83333 mAh
+        // scr-off GNSS-on: 0.12500 + 0.83333 = 0.95833 mAh
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.06944);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.12500 + 0.83333);
+
+        // UID1 =
+        //   scr-on FG: 2500 -> 0.06944 mAh
+        //   scr-off BG: 2500/7500 * 0.95833 = 0.31944 mAh
+        //   scr-off FGS: 1000/7500 * 0.95833 = 0.12777 mAh
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.06944);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.31944);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.12777);
+
+        // UID2 =
+        //   scr-off cached: 4000/7500 * 0.95833 = 0.51111 mAh
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.51111);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+    @Test
+    public void energyConsumerModel() {
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.GNSS, null))
+                .thenReturn(new int[]{ENERGY_CONSUMER_ID});
+        GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor(
+                mStatsRule.getPowerProfile(), mUidResolver);
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
+
+        GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
+        collector.addConsumer(
+                powerStats -> {
+                    processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
+                });
+        collector.setEnabled(true);
+
+        // Establish a baseline
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(new long[]{uCtoUj(10000)});
+        collector.collectAndDeliverStats();
+
+        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
+
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(new long[]{uCtoUj(2_170_000)});
+        collector.collectAndDeliverStats();
+
+        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
+        processor.noteStateChange(stats, buildHistoryItem(7000,
+                GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
+        processor.noteStateChange(stats, buildHistoryItem(8000,
+                GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
+        mStatsRule.setTime(11_000, 11_000);
+        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(new long[]{uCtoUj(3_610_000)});
+        collector.collectAndDeliverStats();
+
+        processor.finish(stats, 11_000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+        statsLayout.fromExtras(descriptor.extras);
+
+        // Total estimated power = 3,600,000 uC = 1.0 mAh
+        // of which 3,000,000 is distributed:
+        //     Screen-on  - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
+        //     Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
+        // and 600,000 was fully with screen off:
+        //     Screen-off - 1440000 uC = 0.4 mAh
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.25);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.35 + 0.4);
+
+        // UID1 =
+        //     2,160,000 uC = 0.6 mAh
+        //     split between three different states
+        //          fg screen-on: 2500/6000
+        //          bg screen-off: 2500/6000
+        //          fgs screen-off: 1000/6000
+        double expectedPower1 = 0.6;
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+        // UID2 =
+        //     1440000 mA-ms = 0.4 mAh
+        //     all in the same state
+        double expectedPower2 = 0.4;
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
+            int uid) {
+        mStatsRule.setTime(timestamp, timestamp);
+        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+        historyItem.time = mMonotonicClock.monotonicTime();
+        historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_GPS_ON_FLAG : 0;
+        if (stateOn) {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
+        } else {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+        }
+        historyItem.eventTag = historyItem.localEventTag;
+        historyItem.eventTag.uid = uid;
+        historyItem.eventTag.string = "gnss";
+        return historyItem;
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, int signalLevel) {
+        mStatsRule.setTime(timestamp, timestamp);
+        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+        historyItem.time = mMonotonicClock.monotonicTime();
+        historyItem.states = BatteryStats.HistoryItem.STATE_GPS_ON_FLAG;
+        historyItem.states2 =
+                signalLevel << BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
+        return historyItem;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            BinaryStatePowerStatsProcessor processor) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessor(processor);
+
+        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
+        PowerComponentAggregatedPowerStats powerComponentStats =
+                aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS);
+        processor.start(powerComponentStats, 0);
+
+        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        return powerComponentStats;
+    }
+
+    private static long uCtoUj(long uc) {
+        return (long) (uc * (double) VOLTAGE_MV / 1000);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
index d4f0d5a..52b33db 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
@@ -593,12 +593,6 @@
         }
 
         @Override
-        public void onMagnificationSystemUIConnectionChanged(boolean connected)
-                throws RemoteException {
-
-        }
-
-        @Override
         public void onMagnificationChanged(int displayId, Region region, MagnificationConfig config)
                 throws RemoteException {
 
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 f482ddc..3ef81fd 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
@@ -28,8 +28,11 @@
 import static com.android.server.testutils.TestUtils.strictMock;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 import static org.mockito.AdditionalMatchers.gt;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -198,6 +201,7 @@
     private final Scroller mMockScroller = spy(new Scroller(mContext));
 
     private boolean mMockMagnificationConnectionState;
+    private boolean mMockOneFingerPanningEnabled;
 
     private OffsettableClock mClock;
     private FullScreenMagnificationGestureHandler mMgh;
@@ -209,8 +213,6 @@
 
     static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 800, 800);
 
-    static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS);
-
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -282,6 +284,8 @@
     @NonNull
     private FullScreenMagnificationGestureHandler newInstance(boolean detectSingleFingerTripleTap,
             boolean detectTwoFingerTripleTap, boolean detectShortcutTrigger) {
+        enableOneFingerPanning(
+                    isWatch() || Flags.enableMagnificationOneFingerPanningGesture());
         FullScreenMagnificationGestureHandler h =
                 new FullScreenMagnificationGestureHandler(
                         mContext,
@@ -297,8 +301,12 @@
                         mMockMagnificationLogger,
                         ViewConfiguration.get(mContext),
                         mMockOneFingerPanningSettingsProvider);
+        // OverscrollHandler is only supported on watches.
+        // @See config_enable_a11y_fullscreen_magnification_overscroll_handler
         if (isWatch()) {
-            enableOneFingerPanning(true);
+            assertNotNull(h.mOverscrollHandler);
+        } else {
+            assertNull(h.mOverscrollHandler);
         }
         mHandler = new TestHandler(h.mDetectingState, mClock) {
             @Override
@@ -836,7 +844,7 @@
 
     @Test
     public void testActionUpNotAtEdge_singlePanningState_detectingState() {
-        assumeTrue(isWatch());
+        assumeTrue(isOneFingerPanningEnabled());
         goFromStateIdleTo(STATE_SINGLE_PANNING);
 
         send(upEvent());
@@ -846,8 +854,7 @@
     }
 
     @Test
-    public void testScroll_SinglePanningDisabled_delegatingState() {
-        assumeTrue(isWatch());
+    public void testScroll_singlePanningDisabled_delegatingState() {
         enableOneFingerPanning(false);
 
         goFromStateIdleTo(STATE_ACTIVATED);
@@ -858,8 +865,54 @@
     }
 
     @Test
+    public void testSingleFingerOverscrollAtLeftEdge_isNotWatch_transitionToDelegatingState() {
+        assumeTrue(isOneFingerPanningEnabled());
+        assumeFalse(isWatch());
+        goFromStateIdleTo(STATE_ACTIVATED);
+        float centerY =
+                (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.bottom) / 2.0f;
+        mFullScreenMagnificationController.setCenter(
+                DISPLAY_0, INITIAL_MAGNIFICATION_BOUNDS.left, centerY, false, 1);
+        final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+        PointF initCoords =
+                new PointF(INITIAL_MAGNIFICATION_BOUNDS.centerX(),
+                        INITIAL_MAGNIFICATION_BOUNDS.centerY());
+        PointF edgeCoords = new PointF(initCoords.x, initCoords.y);
+        edgeCoords.offset(swipeMinDistance, 0);
+
+        allowEventDelegation();
+        swipeAndHold(initCoords, edgeCoords);
+
+        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+        assertTrue(isZoomed());
+    }
+
+    @Test
+    public void testSingleFingerOverscrollAtBottomEdge_isNotWatch_transitionToDelegatingState() {
+        assumeTrue(isOneFingerPanningEnabled());
+        assumeFalse(isWatch());
+        goFromStateIdleTo(STATE_ACTIVATED);
+        float centerX =
+                (INITIAL_MAGNIFICATION_BOUNDS.right + INITIAL_MAGNIFICATION_BOUNDS.left) / 2.0f;
+        mFullScreenMagnificationController.setCenter(
+                DISPLAY_0, centerX, INITIAL_MAGNIFICATION_BOUNDS.bottom, false, 1);
+        final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+        PointF initCoords =
+                new PointF(INITIAL_MAGNIFICATION_BOUNDS.centerX(),
+                        INITIAL_MAGNIFICATION_BOUNDS.centerY());
+        PointF edgeCoords = new PointF(initCoords.x, initCoords.y);
+        edgeCoords.offset(0, -swipeMinDistance);
+
+        allowEventDelegation();
+        swipeAndHold(initCoords, edgeCoords);
+
+        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+        assertTrue(isZoomed());
+    }
+
+    @Test
     @FlakyTest
-    public void testScroll_singleHorizontalPanningAndAtEdge_leftEdgeOverscroll() {
+    public void testSingleFingerOverscrollAtLeftEdge_isWatch_expectedOverscrollState() {
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_SINGLE_PANNING);
         float centerY =
@@ -883,7 +936,7 @@
 
     @Test
     @FlakyTest
-    public void testScroll_singleHorizontalPanningAndAtEdge_rightEdgeOverscroll() {
+    public void testSingleFingerOverscrollAtRightEdge_isWatch_expectedOverscrollState() {
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_SINGLE_PANNING);
         float centerY =
@@ -907,7 +960,7 @@
 
     @Test
     @FlakyTest
-    public void testScroll_singleVerticalPanningAndAtEdge_verticalOverscroll() {
+    public void testSingleFingerOverscrollAtTopEdge_isWatch_expectedOverscrollState() {
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_SINGLE_PANNING);
         float centerX =
@@ -929,7 +982,7 @@
     }
 
     @Test
-    public void testScroll_singlePanningAndAtEdge_noOverscroll() {
+    public void testSingleFingerScrollAtEdge_isWatch_noOverscroll() {
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_SINGLE_PANNING);
         float centerY =
@@ -951,7 +1004,7 @@
     }
 
     @Test
-    public void testScroll_singleHorizontalPanningAndAtEdge_vibrate() {
+    public void testSingleFingerHorizontalScrollAtEdge_isWatch_vibrate() {
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_SINGLE_PANNING);
         mFullScreenMagnificationController.setCenter(
@@ -975,7 +1028,7 @@
     }
 
     @Test
-    public void testScroll_singleVerticalPanningAndAtEdge_doNotVibrate() {
+    public void testSingleFingerVerticalScrollAtEdge_isWatch_doNotVibrate() {
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_SINGLE_PANNING);
         mFullScreenMagnificationController.setCenter(
@@ -1000,7 +1053,7 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_FULLSCREEN_FLING_GESTURE)
-    public void singleFinger_testScrollAfterMagnified_startsFling() {
+    public void testSingleFingerScrollAfterMagnified_startsFling() {
         assumeTrue(isWatch());
         goFromStateIdleTo(STATE_ACTIVATED);
 
@@ -1282,9 +1335,14 @@
     }
 
     private void enableOneFingerPanning(boolean enable) {
+        mMockOneFingerPanningEnabled = enable;
         when(mMockOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()).thenReturn(enable);
     }
 
+    private boolean isOneFingerPanningEnabled() {
+        return mMockOneFingerPanningEnabled;
+    }
+
     private void assertActionsInOrder(List<MotionEvent> actualEvents,
             List<Integer> expectedActions) {
         assertTrue(actualEvents.size() == expectedActions.size());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
index 0de5807..87fe6cf 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -59,7 +59,6 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.FlakyTest;
 
-import com.android.compatibility.common.util.TestUtils;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityTraceManager;
@@ -77,7 +76,6 @@
  */
 public class MagnificationConnectionManagerTest {
 
-    private static final int WAIT_CONNECTION_TIMEOUT_SECOND = 1;
     private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
     private static final int SERVICE_ID = 1;
 
@@ -117,19 +115,17 @@
     private void stubSetConnection(boolean needDelay) {
         doAnswer((InvocationOnMock invocation) -> {
             final boolean connect = (Boolean) invocation.getArguments()[0];
-            // Use post to simulate setConnection() called by another process.
-            final Context context = ApplicationProvider.getApplicationContext();
+            // Simulates setConnection() called by another process.
             if (needDelay) {
+                final Context context = ApplicationProvider.getApplicationContext();
                 context.getMainThreadHandler().postDelayed(
                         () -> {
                             mMagnificationConnectionManager.setConnection(
                                     connect ? mMockConnection.getConnection() : null);
                         }, 10);
             } else {
-                context.getMainThreadHandler().post(() -> {
-                    mMagnificationConnectionManager.setConnection(
-                            connect ? mMockConnection.getConnection() : null);
-                });
+                mMagnificationConnectionManager.setConnection(
+                        connect ? mMockConnection.getConnection() : null);
             }
             return true;
         }).when(mMockStatusBarManagerInternal).requestMagnificationConnection(anyBoolean());
@@ -633,10 +629,9 @@
     }
 
     @Test
-    public void isConnected_requestConnection_expectedValue() throws Exception {
+    public void isConnected_requestConnection_expectedValue() throws RemoteException {
         mMagnificationConnectionManager.requestConnection(true);
-        TestUtils.waitUntil("connection is not ready", WAIT_CONNECTION_TIMEOUT_SECOND,
-                () -> mMagnificationConnectionManager.isConnected());
+        assertTrue(mMagnificationConnectionManager.isConnected());
 
         mMagnificationConnectionManager.requestConnection(false);
         assertFalse(mMagnificationConnectionManager.isConnected());
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
index 8cbed2c..31bf5f0 100644
--- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -61,20 +61,6 @@
     private static final long USAGE_STATS_INTERACTION = 10 * 60 * 1000L;
     private static final long SERVICE_USAGE_INTERACTION = 60 * 1000;
 
-    static class MyOomAdjuster extends OomAdjuster {
-
-        MyOomAdjuster(ActivityManagerService service, ProcessList processList,
-                ActiveUids activeUids) {
-            super(service, processList, activeUids);
-        }
-
-        @Override
-        protected boolean isChangeEnabled(int changeId, ApplicationInfo app,
-                boolean defaultValue) {
-            return true;
-        }
-    }
-
     @BeforeClass
     public static void setUpOnce() {
         sContext = getInstrumentation().getTargetContext();
@@ -99,7 +85,15 @@
             final AppProfiler profiler = mock(AppProfiler.class);
             setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
             setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
-            sService.mOomAdjuster = new MyOomAdjuster(sService, sService.mProcessList, null);
+            final OomAdjuster.Injector injector = new OomAdjuster.Injector(){
+                @Override
+                boolean isChangeEnabled(int changeId, ApplicationInfo app,
+                        boolean defaultValue) {
+                    return true;
+                }
+            };
+            sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null,
+                    injector);
             LocalServices.addService(UsageStatsManagerInternal.class,
                     mock(UsageStatsManagerInternal.class));
             sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java b/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java
new file mode 100644
index 0000000..6d56c41
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/autofill/RequestIdTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.autofill;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RequestIdTest {
+
+    List<Integer> datasetPrimaryNoWrap = new ArrayList<>();
+    List<Integer> datasetPrimaryWrap = new ArrayList<>();
+    List<Integer> datasetSecondaryNoWrap = new ArrayList<>();
+    List<Integer> datasetSecondaryWrap = new ArrayList<>();
+    List<Integer> datasetMixedNoWrap = new ArrayList<>();
+    List<Integer> datasetMixedWrap = new ArrayList<>();
+
+    @Before
+    public void setup() throws Exception {
+      int datasetSize = 300;
+
+        { // Generate primary only ids that do not wrap
+            RequestId requestId = new RequestId(0);
+            for (int i = 0; i < datasetSize; i++) {
+                datasetPrimaryNoWrap.add(requestId.nextId(false));
+            }
+        }
+
+        { // Generate primary only ids that wrap
+            RequestId requestId = new RequestId(0xff00);
+            for (int i = 0; i < datasetSize; i++) {
+                datasetPrimaryWrap.add(requestId.nextId(false));
+            }
+        }
+
+        { // Generate SECONDARY only ids that do not wrap
+            RequestId requestId = new RequestId(0);
+            for (int i = 0; i < datasetSize; i++) {
+                datasetSecondaryNoWrap.add(requestId.nextId(true));
+            }
+        }
+
+        { // Generate SECONDARY only ids that wrap
+            RequestId requestId = new RequestId(0xff00);
+            for (int i = 0; i < datasetSize; i++) {
+                datasetSecondaryWrap.add(requestId.nextId(true));
+            }
+        }
+
+        { // Generate MIXED only ids that do not wrap
+            RequestId requestId = new RequestId(0);
+            for (int i = 0; i < datasetSize; i++) {
+                datasetMixedNoWrap.add(requestId.nextId(i % 2 != 0));
+            }
+        }
+
+        { // Generate MIXED only ids that wrap
+            RequestId requestId = new RequestId(0xff00);
+            for (int i = 0; i < datasetSize; i++) {
+                datasetMixedWrap.add(requestId.nextId(i % 2 != 0));
+            }
+        }
+    }
+
+    @Test
+    public void testRequestIdLists() {
+        for (int id : datasetPrimaryNoWrap) {
+            assertThat(RequestId.isSecondaryProvider(id)).isFalse();
+            assertThat(id >= 0).isTrue();
+            assertThat(id < 0xffff).isTrue();
+        }
+
+        for (int id : datasetPrimaryWrap) {
+            assertThat(RequestId.isSecondaryProvider(id)).isFalse();
+            assertThat(id >= 0).isTrue();
+            assertThat(id < 0xffff).isTrue();
+        }
+
+        for (int id : datasetSecondaryNoWrap) {
+            assertThat(RequestId.isSecondaryProvider(id)).isTrue();
+            assertThat(id >= 0).isTrue();
+            assertThat(id < 0xffff).isTrue();
+        }
+
+        for (int id : datasetSecondaryWrap) {
+            assertThat(RequestId.isSecondaryProvider(id)).isTrue();
+            assertThat(id >= 0).isTrue();
+            assertThat(id < 0xffff).isTrue();
+        }
+    }
+
+    @Test
+    public void testRequestIdGeneration() {
+        RequestId requestId = new RequestId(0);
+
+        // Large Primary
+        for (int i = 0; i < 100000; i++) {
+            int y = requestId.nextId(false);
+            assertThat(RequestId.isSecondaryProvider(y)).isFalse();
+            assertThat(y >= 0).isTrue();
+            assertThat(y < 0xffff).isTrue();
+        }
+
+        // Large Secondary
+        requestId = new RequestId(0);
+        for (int i = 0; i < 100000; i++) {
+            int y = requestId.nextId(true);
+            assertThat(RequestId.isSecondaryProvider(y)).isTrue();
+            assertThat(y >= 0).isTrue();
+            assertThat(y < 0xffff).isTrue();
+        }
+
+        // Large Mixed
+        requestId = new RequestId(0);
+        for (int i = 0; i < 50000; i++) {
+            int y = requestId.nextId(i % 2 != 0);
+            assertThat(RequestId.isSecondaryProvider(y)).isEqualTo(i % 2 == 0);
+            assertThat(y >= 0).isTrue();
+            assertThat(y < 0xffff).isTrue();
+        }
+    }
+
+    @Test
+    public void testGetLastRequestId() {
+        // In this test, request ids are generated FIFO, so the last entry is also the last
+        // request
+
+        { // Primary no wrap
+          int lastIdIndex = datasetPrimaryNoWrap.size() - 1;
+          int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryNoWrap);
+          assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+        }
+
+        { // Primary wrap
+            int lastIdIndex = datasetPrimaryWrap.size() - 1;
+            int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetPrimaryWrap);
+            assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+        }
+
+        { // Secondary no wrap
+            int lastIdIndex = datasetSecondaryNoWrap.size() - 1;
+            int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetSecondaryNoWrap);
+            assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+        }
+
+        { // Secondary wrap
+            int lastIdIndex = datasetSecondaryWrap.size() - 1;
+            int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetSecondaryWrap);
+            assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+        }
+
+        { // Mixed no wrap
+            int lastIdIndex = datasetMixedNoWrap.size() - 1;
+            int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetMixedNoWrap);
+            assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+        }
+
+        { // Mixed wrap
+            int lastIdIndex = datasetMixedWrap.size() - 1;
+            int lastComputedIdIndex = RequestId.getLastRequestIdIndex(datasetMixedWrap);
+            assertThat(lastIdIndex).isEqualTo(lastComputedIdIndex);
+        }
+
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index 1eaa170..bc2fd73 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -22,10 +22,11 @@
 import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED;
 import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED;
 import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGATIVE;
+import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_USER_CANCEL;
 
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED;
-import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED;
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_PAUSED;
+import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED;
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING;
 import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI;
 
@@ -50,9 +51,9 @@
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustManager;
-import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.ComponentInfoInternal;
@@ -70,9 +71,12 @@
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.security.KeyStoreAuthorization;
+import android.testing.TestableContext;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.internal.R;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.biometrics.log.BiometricContext;
@@ -80,6 +84,7 @@
 import com.android.server.biometrics.log.OperationContextExt;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -95,8 +100,12 @@
 
     private static final String TEST_PACKAGE = "test_package";
     private static final long TEST_REQUEST_ID = 22;
+    private static final String ACQUIRED_STRING = "test_acquired_info_callback";
+    private static final String ACQUIRED_STRING_VENDOR = "test_acquired_info_callback_vendor";
 
-    @Mock private Context mContext;
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
     @Mock private Resources mResources;
     @Mock private BiometricContext mBiometricContext;
     @Mock private ITrustManager mTrustManager;
@@ -110,6 +119,7 @@
     @Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver;
     @Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger;
     @Mock private BiometricCameraManager mBiometricCameraManager;
+    @Mock private BiometricManager mBiometricManager;
 
     private Random mRandom;
     private IBinder mToken;
@@ -121,7 +131,11 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        when(mContext.getResources()).thenReturn(mResources);
+        mContext.addMockSystemService(BiometricManager.class, mBiometricManager);
+        mContext.getOrCreateTestableResources().addOverride(R.string.fingerprint_acquired_partial,
+                ACQUIRED_STRING);
+        mContext.getOrCreateTestableResources().addOverride(R.array.fingerprint_acquired_vendor,
+                new String[]{ACQUIRED_STRING_VENDOR});
         when(mClientReceiver.asBinder()).thenReturn(mock(Binder.class));
         when(mBiometricContext.updateContext(any(), anyBoolean()))
                 .thenAnswer(invocation -> invocation.getArgument(0));
@@ -499,8 +513,6 @@
 
     @Test
     public void testCallbackOnAcquired() throws RemoteException {
-        final String acquiredStr = "test_acquired_info_callback";
-        final String acquiredStrVendor = "test_acquired_info_callback_vendor";
         setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_REAR);
 
         final AuthSession session = createAuthSession(mSensors,
@@ -510,18 +522,15 @@
                 0 /* operationId */,
                 0 /* userId */);
 
-        when(mContext.getString(com.android.internal.R.string.fingerprint_acquired_partial))
-            .thenReturn(acquiredStr);
         session.onAcquired(0, FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL, 0);
-        verify(mStatusBarService).onBiometricHelp(anyInt(), eq(acquiredStr));
-        verify(mClientReceiver).onAcquired(eq(1), eq(acquiredStr));
+        verify(mStatusBarService).onBiometricHelp(anyInt(), eq(ACQUIRED_STRING));
+        verify(mClientReceiver).onAcquired(eq(1), eq(ACQUIRED_STRING));
 
-        when(mResources.getStringArray(com.android.internal.R.array.fingerprint_acquired_vendor))
-            .thenReturn(new String[]{acquiredStrVendor});
         session.onAcquired(0, FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR, 0);
-        verify(mStatusBarService).onBiometricHelp(anyInt(), eq(acquiredStrVendor));
+        verify(mStatusBarService).onBiometricHelp(anyInt(), eq(ACQUIRED_STRING_VENDOR));
         verify(mClientReceiver).onAcquired(
-                eq(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR_BASE), eq(acquiredStrVendor));
+                eq(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR_BASE),
+                eq(ACQUIRED_STRING_VENDOR));
     }
 
     @Test
@@ -665,6 +674,87 @@
         verify(mStatusBarService, never()).onBiometricError(anyInt(), anyInt(), anyInt());
     }
 
+    @Test
+    public void onAuthReceivedWhileWaitingForConfirmation_SFPS() throws Exception {
+        setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_POWER_BUTTON);
+        setupFace(1 /* id */, false, mock(IBiometricAuthenticator.class));
+        final long operationId = 123;
+        final int userId = 10;
+        final AuthSession session = createAuthSession(mSensors,
+                false /* checkDevicePolicyManager */,
+                Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
+                operationId,
+                userId);
+        session.goToInitialState();
+        for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+            session.onCookieReceived(
+                    session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie());
+        }
+        session.onDialogAnimatedIn(true /* startFingerprintNow */);
+
+        // Face succeeds
+        session.onAuthenticationSucceeded(1, true, null);
+        verify(mStatusBarService).onBiometricAuthenticated(TYPE_FACE);
+        for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) {
+            if (sensor.modality == FingerprintSensorProperties.TYPE_POWER_BUTTON) {
+                assertEquals(BiometricSensor.STATE_AUTHENTICATING, sensor.getSensorState());
+            }
+        }
+
+        // SFPS succeeds
+        session.onAuthenticationSucceeded(0, true, null);
+        verify(mStatusBarService).onBiometricAuthenticated(TYPE_FINGERPRINT);
+    }
+
+    @Test
+    public void onDialogDismissedResetLockout_Confirmed() throws Exception {
+        setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_POWER_BUTTON);
+        setupFace(1 /* id */, false, mock(IBiometricAuthenticator.class));
+        final long operationId = 123;
+        final int userId = 10;
+        final AuthSession session = createAuthSession(mSensors,
+                false /* checkDevicePolicyManager */,
+                Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
+                operationId,
+                userId);
+        session.goToInitialState();
+        session.onDialogAnimatedIn(true /* startFingerprintNow */);
+
+        // Face succeeds
+        session.onAuthenticationSucceeded(1, true, new byte[1]);
+
+        // Dismiss through confirmation
+        session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRMED, null);
+
+        verify(mBiometricManager).resetLockoutTimeBound(any(), any(), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void onDialogDismissedResetLockout_Cancelled() throws Exception {
+        setupFingerprint(0 /* id */, FingerprintSensorProperties.TYPE_POWER_BUTTON);
+        setupFace(1 /* id */, false, mock(IBiometricAuthenticator.class));
+        final long operationId = 123;
+        final int userId = 10;
+        final AuthSession session = createAuthSession(mSensors,
+                false /* checkDevicePolicyManager */,
+                Authenticators.BIOMETRIC_STRONG,
+                TEST_REQUEST_ID,
+                operationId,
+                userId);
+        session.goToInitialState();
+        session.onDialogAnimatedIn(true /* startFingerprintNow */);
+
+        // Face succeeds
+        session.onAuthenticationSucceeded(1, true, new byte[1]);
+
+        // User cancel after success
+        session.onDialogDismissed(DISMISSED_REASON_USER_CANCEL, null);
+
+        verify(mBiometricManager).resetLockoutTimeBound(any(), any(), anyInt(), anyInt(), any());
+    }
+
     // TODO (b/208484275) : Enable these tests
     // @Test
     // public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
index 9873805..2d4dbb7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java
@@ -92,6 +92,7 @@
 import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -445,21 +446,61 @@
         );
     }
 
+    @Test
+    public void testResetLockoutOnAuthSuccess_nonBiometricPrompt() throws RemoteException {
+        FaceAuthenticationClient client = createClient(false);
+        client.start(mCallback);
+        client.onAuthenticated(new Face("friendly", 1 /* faceId */,
+                2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
+
+        verify(mBiometricManager).resetLockoutTimeBound(eq(mToken), eq(mContext.getOpPackageName()),
+                anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void testNoResetLockoutOnAuthFailure_nonBiometricPrompt() throws RemoteException {
+        FaceAuthenticationClient client = createClient(false);
+        client.start(mCallback);
+        client.onAuthenticated(new Face("friendly", 1 /* faceId */,
+                2 /* deviceId */), false /* authenticated */, createHardwareAuthToken());
+
+        verify(mBiometricManager, never()).resetLockoutTimeBound(eq(mToken),
+                eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void testNoResetLockoutOnAuthSuccess_BiometricPrompt() throws RemoteException {
+        FaceAuthenticationClient client = createClient(true);
+        client.start(mCallback);
+        client.onAuthenticated(new Face("friendly", 1 /* faceId */,
+                2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
+
+        verify(mBiometricManager, never()).resetLockoutTimeBound(eq(mToken),
+                eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
+    }
+
     private FaceAuthenticationClient createClient() throws RemoteException {
         return createClient(2 /* version */, mClientMonitorCallbackConverter,
-                false /* allowBackgroundAuthentication */,
+                false /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
+                null /* lockoutTracker */);
+    }
+
+    private FaceAuthenticationClient createClient(boolean isBiometricPrompt)
+            throws RemoteException {
+        return createClient(2 /* version */, mClientMonitorCallbackConverter,
+                true /* allowBackgroundAuthentication */, isBiometricPrompt,
                 null /* lockoutTracker */);
     }
 
     private FaceAuthenticationClient createClientWithNullListener() throws RemoteException {
         return createClient(2 /* version */, null /* listener */,
-                true /* allowBackgroundAuthentication */,
+                false /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
                 null /* lockoutTracker */);
     }
 
     private FaceAuthenticationClient createClient(int version) throws RemoteException {
         return createClient(version, mClientMonitorCallbackConverter,
-                false /* allowBackgroundAuthentication */,
+                false /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
                 null /* lockoutTracker */);
     }
 
@@ -468,12 +509,14 @@
         return createClient(0 /* version */,
                 mClientMonitorCallbackConverter,
                 true /* allowBackgroundAuthentication */,
+                true /* isBiometricPrompt */,
                 lockoutTracker);
     }
 
     private FaceAuthenticationClient createClient(int version,
             ClientMonitorCallbackConverter listener,
             boolean allowBackgroundAuthentication,
+            boolean isBiometricPrompt,
             LockoutTracker lockoutTracker) throws RemoteException {
         when(mHal.getInterfaceVersion()).thenReturn(version);
 
@@ -488,7 +531,7 @@
                 .build();
         return new FaceAuthenticationClient(mContext, () -> aidl, mToken,
                 2 /* requestId */, listener, OP_ID,
-                false /* restricted */, options, 4 /* cookie */,
+                false /* restricted */, options, isBiometricPrompt ? 4 : 0 /* cookie */,
                 false /* requireConfirmation */,
                 mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
                 mUsageStats, lockoutTracker, allowBackgroundAuthentication,
@@ -500,4 +543,8 @@
             }
         };
     }
+
+    private ArrayList<Byte> createHardwareAuthToken() {
+        return new ArrayList<>(Collections.nCopies(69, Byte.valueOf("0")));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 40de1b2..ecd799f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -98,6 +98,7 @@
 
 import java.time.Clock;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -320,7 +321,7 @@
     }
 
     @Test
-    public void luxProbeNotEnabledOnStartWhenNotWake() throws RemoteException {
+    public void luxProbeDisabledOnStartWhenNotWake() throws RemoteException {
         luxProbeEnabledOnStart(false /* isAwake */);
     }
 
@@ -337,6 +338,7 @@
                 .getValue().toAidlContext());
 
         verify(mLuxProbe, isAwake ? times(1) : never()).enable();
+        verify(mLuxProbe, isAwake ? never() : times(1)).disable();
     }
 
     @Test
@@ -607,6 +609,45 @@
     }
 
     @Test
+    public void testResetLockoutOnAuthSuccess_nonBiometricPrompt() throws RemoteException {
+        final FingerprintAuthenticationClient client = createClient(1 /* version */,
+                true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
+                mClientMonitorCallbackConverter, mLockoutTracker);
+        client.start(mCallback);
+        client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+                2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
+
+        verify(mBiometricManager).resetLockoutTimeBound(eq(mToken), eq(mContext.getOpPackageName()),
+                anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void testNoResetLockoutOnAuthFailure_nonBiometricPrompt() throws RemoteException {
+        final FingerprintAuthenticationClient client = createClient(1 /* version */,
+                true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
+                mClientMonitorCallbackConverter, mLockoutTracker);
+        client.start(mCallback);
+        client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+                2 /* deviceId */), false /* authenticated */, createHardwareAuthToken());
+
+        verify(mBiometricManager, never()).resetLockoutTimeBound(eq(mToken),
+                eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void testNoResetLockoutOnAuthSuccess_BiometricPrompt() throws RemoteException {
+        final FingerprintAuthenticationClient client = createClient(1 /* version */,
+                true /* allowBackgroundAuthentication */, true /* isBiometricPrompt */,
+                mClientMonitorCallbackConverter, mLockoutTracker);
+        client.start(mCallback);
+        client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
+                2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
+
+        verify(mBiometricManager, never()).resetLockoutTimeBound(eq(mToken),
+                eq(mContext.getOpPackageName()), anyInt(), anyInt(), any());
+    }
+
+    @Test
     public void testOnAuthenticatedFalseWhenListenerIsNull() throws RemoteException {
         final FingerprintAuthenticationClient client = createClientWithNullListener();
         client.start(mCallback);
@@ -629,11 +670,11 @@
     @Test
     public void testLockoutTracker_authSuccess() throws RemoteException {
         final FingerprintAuthenticationClient client = createClient(1 /* version */,
-                true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
-                mLockoutTracker);
+                true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
+                mClientMonitorCallbackConverter, mLockoutTracker);
         client.start(mCallback);
         client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
-                2 /* deviceId */), true /* authenticated */, new ArrayList<>());
+                2 /* deviceId */), true /* authenticated */, createHardwareAuthToken());
 
         verify(mLockoutTracker).resetFailedAttemptsForUser(true, USER_ID);
         verify(mLockoutTracker, never()).addFailedAttemptForUser(anyInt());
@@ -642,11 +683,11 @@
     @Test
     public void testLockoutTracker_authFailed() throws RemoteException {
         final FingerprintAuthenticationClient client = createClient(1 /* version */,
-                true /* allowBackgroundAuthentication */, mClientMonitorCallbackConverter,
-                mLockoutTracker);
+                true /* allowBackgroundAuthentication */, false /* isBiometricPrompt */,
+                mClientMonitorCallbackConverter, mLockoutTracker);
         client.start(mCallback);
         client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */,
-                2 /* deviceId */), false /* authenticated */, new ArrayList<>());
+                2 /* deviceId */), false /* authenticated */, createHardwareAuthToken());
 
         verify(mLockoutTracker, never()).resetFailedAttemptsForUser(anyBoolean(), anyInt());
         verify(mLockoutTracker).addFailedAttemptForUser(USER_ID);
@@ -654,27 +695,31 @@
 
     private FingerprintAuthenticationClient createClient() throws RemoteException {
         return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
+                true /* isBiometricPrompt */,
                 mClientMonitorCallbackConverter, null);
     }
 
     private FingerprintAuthenticationClient createClientWithoutBackgroundAuth()
             throws RemoteException {
         return createClient(100 /* version */, false /* allowBackgroundAuthentication */,
-                mClientMonitorCallbackConverter, null);
+                true /* isBiometricPrompt */, mClientMonitorCallbackConverter, null);
     }
 
     private FingerprintAuthenticationClient createClient(int version) throws RemoteException {
         return createClient(version, true /* allowBackgroundAuthentication */,
+                true /* isBiometricPrompt */,
                 mClientMonitorCallbackConverter, null);
     }
 
     private FingerprintAuthenticationClient createClientWithNullListener() throws RemoteException {
         return createClient(100 /* version */, true /* allowBackgroundAuthentication */,
-                null, /* listener */null);
+                true /* isBiometricPrompt */,
+                /* listener */null, null);
     }
 
     private FingerprintAuthenticationClient createClient(int version,
-            boolean allowBackgroundAuthentication, ClientMonitorCallbackConverter listener,
+            boolean allowBackgroundAuthentication, boolean isBiometricPrompt,
+            ClientMonitorCallbackConverter listener,
             LockoutTracker lockoutTracker)
             throws RemoteException {
         when(mHal.getInterfaceVersion()).thenReturn(version);
@@ -686,7 +731,8 @@
                 .setSensorId(SENSOR_ID)
                 .build();
         return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken, REQUEST_ID,
-                listener, OP_ID, false /* restricted */, options, 4 /* cookie */,
+                listener, OP_ID, false /* restricted */, options,
+                isBiometricPrompt ? 4 : 0 /* cookie */,
                 false /* requireConfirmation */, mBiometricLogger, mBiometricContext,
                 true /* isStrongBiometric */, null /* taskStackListener */, mUdfpsOverlayController,
                 mAuthenticationStateListeners, allowBackgroundAuthentication, mSensorProps,
@@ -697,4 +743,8 @@
             }
         };
     }
+
+    private ArrayList<Byte> createHardwareAuthToken() {
+        return new ArrayList<>(Collections.nCopies(69, Byte.valueOf("0")));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 4505a8b..627ca03 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -95,7 +95,7 @@
         mVirtualCameraController = new VirtualCameraController(mVirtualCameraServiceMock,
                 DEVICE_POLICY_CUSTOM, DEVICE_ID);
         when(mVirtualCameraServiceMock.registerCamera(any(), any(), anyInt())).thenReturn(true);
-        when(mVirtualCameraServiceMock.getCameraId(any())).thenReturn(0);
+        when(mVirtualCameraServiceMock.getCameraId(any())).thenReturn("0");
     }
 
     @After
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageTest.java
old mode 100755
new mode 100644
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 3cab75b..3d68849 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -18,11 +18,13 @@
 
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_STACK;
+import static android.app.ActivityManager.MAX_PROCESS_STATE;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
 import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
@@ -165,9 +167,11 @@
 import android.os.PowerSaveState;
 import android.os.RemoteException;
 import android.os.SimpleClock;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -2158,7 +2162,8 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
-    public void testBackgroundChainOnProcStateChange() throws Exception {
+    @RequiresFlagsDisabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN)
+    public void testBackgroundChainOnProcStateChangeSameDelay() throws Exception {
         // initialization calls setFirewallChainEnabled, so we want to reset the invocations.
         clearInvocations(mNetworkManager);
 
@@ -2186,6 +2191,59 @@
     }
 
     @Test
+    @RequiresFlagsEnabled({
+            Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE,
+            Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN
+    })
+    public void testBackgroundChainOnProcStateChangeDifferentDelays() throws Exception {
+        // The app will be blocked when there is no prior proc-state.
+        assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
+
+        // Tweak delays to avoid waiting too long in tests.
+        mService.mBackgroundRestrictionShortDelayMs = 50;
+        mService.mBackgroundRestrictionLongDelayMs = 1000;
+
+        int procStateSeq = 231; // Any arbitrary starting sequence.
+        for (int ps = BACKGROUND_THRESHOLD_STATE; ps <= MAX_PROCESS_STATE; ps++) {
+            clearInvocations(mNetworkManager);
+
+            // Make sure app is in correct process-state to access network.
+            callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, procStateSeq++);
+            verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A,
+                    FIREWALL_RULE_ALLOW);
+            assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+
+            // Now put the app into the background and test that it eventually loses network.
+            callAndWaitOnUidStateChanged(UID_A, ps, procStateSeq++);
+
+            final long uidStateChangeTime = SystemClock.uptimeMillis();
+            if (ps <= PROCESS_STATE_LAST_ACTIVITY) {
+                // Verify that the app is blocked after long delay but not after short delay.
+                waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1);
+                verify(mNetworkManager, never()).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND,
+                        UID_A, FIREWALL_RULE_DEFAULT);
+                assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+
+                final long timeUntilLongDelay = uidStateChangeTime
+                        + mService.mBackgroundRestrictionLongDelayMs - SystemClock.uptimeMillis();
+                assertTrue("No time left to verify long delay in background transition",
+                        timeUntilLongDelay >= 0);
+
+                waitForDelayedMessageOnHandler(timeUntilLongDelay + 1);
+                verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A,
+                        FIREWALL_RULE_DEFAULT);
+                assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
+            } else {
+                // Verify that the app is blocked after short delay.
+                waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1);
+                verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A,
+                        FIREWALL_RULE_DEFAULT);
+                assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
+            }
+        }
+    }
+
+    @Test
     @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
     public void testBackgroundChainOnAllowlistChange() throws Exception {
         // initialization calls setFirewallChainEnabled, so we want to reset the invocations.
@@ -2881,6 +2939,11 @@
         }
     }
 
+    /**
+     * This posts a blocking message to the service handler with the given delayMs and waits for it
+     * to complete. This ensures that all messages posted before the given delayMs will also
+     * have been executed before this method returns and can be verified in subsequent code.
+     */
     private void waitForDelayedMessageOnHandler(long delayMs) throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(1);
         mService.getHandlerForTesting().postDelayed(latch::countDown, delayMs);
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 3685afd..6d79ae4 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -68,7 +68,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -514,7 +513,7 @@
     @Test
     public void testTemperatureWatcherGetSlopeOf() throws RemoteException {
         TemperatureWatcher watcher = mService.mTemperatureWatcher;
-        List<TemperatureWatcher.Sample> samples = new LinkedList<>();
+        List<TemperatureWatcher.Sample> samples = new ArrayList<>();
         for (int i = 0; i < 30; ++i) {
             samples.add(watcher.createSampleForTesting(i, (float) (i / 2 * 2)));
         }
@@ -539,7 +538,7 @@
     public void testTemperatureWatcherGetForecast() throws RemoteException {
         TemperatureWatcher watcher = mService.mTemperatureWatcher;
 
-        LinkedList<TemperatureWatcher.Sample> samples = new LinkedList<>();
+        ArrayList<TemperatureWatcher.Sample> samples = new ArrayList<>();
 
         // Add a single sample
         samples.add(watcher.createSampleForTesting(0, 25.0f));
diff --git a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
index 32c429e..bf47816 100644
--- a/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/hint/HintManagerServiceTest.java
@@ -46,6 +46,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.hardware.power.ChannelConfig;
 import android.hardware.power.IPower;
 import android.hardware.power.SessionConfig;
@@ -131,6 +133,7 @@
         makeWorkDuration(2L, 13L, 2L, 0L, 6L),
         makeWorkDuration(2L, 13L, 2L, 8L, 0L),
     };
+    private static final String TEST_APP_NAME = "com.android.test.app";
 
     @Mock
     private Context mContext;
@@ -140,12 +143,15 @@
     private IPower mIPowerMock;
     @Mock
     private ActivityManagerInternal mAmInternalMock;
+    @Mock
+    private PackageManager mMockPackageManager;
     @Rule
     public final CheckFlagsRule mCheckFlagsRule =
             DeviceFlagsValueProvider.createCheckFlagsRule();
 
     private HintManagerService mService;
     private ChannelConfig mConfig;
+    private ApplicationInfo mApplicationInfo;
 
     private static Answer<Long> fakeCreateWithConfig(Long ptr, Long sessionId) {
         return new Answer<Long>() {
@@ -162,6 +168,12 @@
         mConfig = new ChannelConfig();
         mConfig.readFlagBitmask = 1;
         mConfig.writeFlagBitmask = 2;
+        mApplicationInfo = new ApplicationInfo();
+        mApplicationInfo.category = ApplicationInfo.CATEGORY_GAME;
+        when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
+        when(mMockPackageManager.getNameForUid(anyInt())).thenReturn(TEST_APP_NAME);
+        when(mMockPackageManager.getApplicationInfo(eq(TEST_APP_NAME), anyInt()))
+                .thenReturn(mApplicationInfo);
         when(mNativeWrapperMock.halGetHintSessionPreferredRate())
                 .thenReturn(DEFAULT_HINT_PREFERRED_RATE);
         when(mNativeWrapperMock.halCreateHintSession(eq(TGID), eq(UID), eq(SESSION_TIDS_A),
@@ -560,6 +572,44 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_POWERHINT_THREAD_CLEANUP)
+    public void testNoCleanupDeadThreadsForPrevPowerHalVersion() throws Exception {
+        reset(mIPowerMock);
+        when(mIPowerMock.getInterfaceVersion()).thenReturn(3);
+        HintManagerService service = createService();
+        IBinder token = new Binder();
+        int threadCount = 2;
+
+        // session 1 has 2 non-isolated tids
+        long sessionPtr1 = 111;
+        CountDownLatch stopLatch1 = new CountDownLatch(1);
+        int[] tids1 = createThreads(threadCount, stopLatch1);
+        when(mNativeWrapperMock.halCreateHintSessionWithConfig(eq(TGID), eq(UID), eq(tids1),
+                eq(DEFAULT_TARGET_DURATION), anyInt(), any(SessionConfig.class)))
+                .thenReturn(sessionPtr1);
+        AppHintSession session1 = (AppHintSession) service.getBinderServiceInstance()
+                .createHintSessionWithConfig(token, tids1, DEFAULT_TARGET_DURATION,
+                        SessionTag.OTHER, new SessionConfig());
+        assertNotNull(session1);
+
+        // trigger UID state change by making the process foreground->background, but because the
+        // power hal version is too low, this should result in no cleanup as setThreads don't fire.
+        service.mUidObserver.onUidStateChanged(UID,
+                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
+        service.mUidObserver.onUidStateChanged(UID,
+                ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
+        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500) + TimeUnit.MILLISECONDS.toNanos(
+                CLEAN_UP_UID_DELAY_MILLIS));
+        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
+        reset(mNativeWrapperMock);
+        // this should resume but not update the threads as no cleanup was performed
+        service.mUidObserver.onUidStateChanged(UID,
+                ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
+        verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
+    }
+
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_POWERHINT_THREAD_CLEANUP)
     public void testCleanupDeadThreads() throws Exception {
         HintManagerService service = createService();
         IBinder token = new Binder();
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index f221b75..148c968 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -18,6 +18,20 @@
 
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+import static android.app.StatusBarManager.DISABLE2_MASK;
+import static android.app.StatusBarManager.DISABLE2_NONE;
+import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS;
+import static android.app.StatusBarManager.DISABLE_BACK;
+import static android.app.StatusBarManager.DISABLE_CLOCK;
+import static android.app.StatusBarManager.DISABLE_HOME;
+import static android.app.StatusBarManager.DISABLE_MASK;
+import static android.app.StatusBarManager.DISABLE_NONE;
+import static android.app.StatusBarManager.DISABLE_RECENT;
+import static android.app.StatusBarManager.DISABLE_SEARCH;
+import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
@@ -137,6 +151,7 @@
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
 
         when(mMockStatusBar.asBinder()).thenReturn(mMockStatusBar);
+        when(mMockStatusBar.isBinderAlive()).thenReturn(true);
         when(mApplicationInfo.loadLabel(any())).thenReturn(APP_NAME);
         mockHandleIncomingUser();
 
@@ -722,6 +737,369 @@
         verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
     }
 
+    @Test
+    public void testGetDisableFlags() throws Exception {
+        String packageName = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        mStatusBarManagerService.disable(DISABLE_NONE, mMockStatusBar, packageName);
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+    }
+
+    @Test
+    public void testSetHomeDisabled() throws Exception {
+        int expectedFlags = DISABLE_MASK & DISABLE_HOME;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        // disable
+        mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+        // check that disable works
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+    }
+
+    @Test
+    public void testSetSystemInfoDisabled() throws Exception {
+        int expectedFlags = DISABLE_MASK & DISABLE_SYSTEM_INFO;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        // disable
+        mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+    }
+
+    @Test
+    public void testSetRecentDisabled() throws Exception {
+        int expectedFlags = DISABLE_MASK & DISABLE_RECENT;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        // disable
+        mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+    }
+
+    @Test
+    public void testSetBackDisabled() throws Exception {
+        int expectedFlags = DISABLE_MASK & DISABLE_BACK;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        // disable
+        mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+    }
+
+    @Test
+    public void testSetClockDisabled() throws Exception {
+        int expectedFlags = DISABLE_MASK & DISABLE_CLOCK;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        // disable home
+        mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+    }
+
+    @Test
+    public void testSetSearchDisabled() throws Exception {
+        int expectedFlags = DISABLE_MASK & DISABLE_SEARCH;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+                userId)[0]);
+        // disable
+        mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+    }
+
+    @Test
+    public void testSetQuickSettingsDisabled2() throws Exception {
+        int expectedFlags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE2_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+        // disable
+        mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testSetSystemIconsDisabled2() throws Exception {
+        int expectedFlags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+                userId)[1]);
+        // disable
+        mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testSetNotificationShadeDisabled2() throws Exception {
+        int expectedFlags = DISABLE2_MASK & DISABLE2_NOTIFICATION_SHADE;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+                userId)[1]);
+        // disable
+        mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+
+    @Test
+    public void testSetGlobalActionsDisabled2() throws Exception {
+        int expectedFlags = DISABLE2_MASK & DISABLE2_GLOBAL_ACTIONS;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+                userId)[1]);
+        // disable
+        mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testSetRotateSuggestionsDisabled2() throws Exception {
+        int expectedFlags = DISABLE2_MASK & DISABLE2_ROTATE_SUGGESTIONS;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+        // disable
+        mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testSetTwoDisable2Flags() throws Exception {
+        int expectedFlags = DISABLE2_MASK & DISABLE2_ROTATE_SUGGESTIONS & DISABLE2_QUICK_SETTINGS;
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+        // disable
+        mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testSetTwoDisableFlagsRemoveOne() throws Exception {
+        int twoFlags = DISABLE_MASK & DISABLE_HOME & DISABLE_BACK;
+        int expectedFlag = DISABLE_MASK & DISABLE_HOME;
+
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        // disable
+        mStatusBarManagerService.disable(twoFlags, mMockStatusBar, pkg);
+        mStatusBarManagerService.disable(DISABLE_NONE, mMockStatusBar, pkg);
+        mStatusBarManagerService.disable(expectedFlag, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlag,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+    }
+
+    @Test
+    public void testSetTwoDisable2FlagsRemoveOne() throws Exception {
+        int twoFlags = DISABLE2_MASK & DISABLE2_ROTATE_SUGGESTIONS & DISABLE2_QUICK_SETTINGS;
+        int expectedFlag = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
+
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+                userId)[1]);
+        // disable
+        mStatusBarManagerService.disable2(twoFlags, mMockStatusBar, pkg);
+        mStatusBarManagerService.disable2(DISABLE2_NONE, mMockStatusBar, pkg);
+        mStatusBarManagerService.disable2(expectedFlag, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(expectedFlag,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testDisableBothFlags() throws Exception {
+        int disableFlags = DISABLE_MASK & DISABLE_BACK & DISABLE_HOME;
+        int disable2Flags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS & DISABLE2_ROTATE_SUGGESTIONS;
+
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE, mStatusBarManagerService.getDisableFlags(mMockStatusBar,
+                userId)[0]);
+        assertEquals(DISABLE2_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+        // disable
+        mStatusBarManagerService.disable(disableFlags, mMockStatusBar, pkg);
+        mStatusBarManagerService.disable2(disable2Flags, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(disableFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        assertEquals(disable2Flags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testDisableBothFlagsEnable1Flags() throws Exception {
+        int disableFlags = DISABLE_MASK & DISABLE_BACK & DISABLE_HOME;
+        int disable2Flags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS & DISABLE2_ROTATE_SUGGESTIONS;
+
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        assertEquals(DISABLE2_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+        // disable
+        mStatusBarManagerService.disable(disableFlags, mMockStatusBar, pkg);
+        mStatusBarManagerService.disable2(disable2Flags, mMockStatusBar, pkg);
+        // re-enable one
+        mStatusBarManagerService.disable(DISABLE_NONE, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        assertEquals(disable2Flags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testDisableBothFlagsEnable2Flags() throws Exception {
+        int disableFlags = DISABLE_MASK & DISABLE_BACK & DISABLE_HOME;
+        int disable2Flags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS & DISABLE2_ROTATE_SUGGESTIONS;
+
+        String pkg = mContext.getPackageName();
+        int userId = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(userId);
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        assertEquals(DISABLE2_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+        // disable
+        mStatusBarManagerService.disable(disableFlags, mMockStatusBar, pkg);
+        mStatusBarManagerService.disable2(disable2Flags, mMockStatusBar, pkg);
+        // re-enable one
+        mStatusBarManagerService.disable2(DISABLE_NONE, mMockStatusBar, pkg);
+        // check that right flag is disabled
+        assertEquals(disableFlags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[0]);
+        assertEquals(DISABLE2_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, userId)[1]);
+    }
+
+    @Test
+    public void testDifferentUsersDisable() throws Exception {
+        int user1Id = 0;
+        mockUidCheck();
+        mockCurrentUserCheck(user1Id);
+        int user2Id = 14;
+        mockComponentInfo(user2Id);
+        mockEverything(user2Id);
+
+        int expectedUser1Flags = DISABLE_MASK & DISABLE_BACK;
+        int expectedUser2Flags = DISABLE_MASK & DISABLE_HOME;
+        String pkg = mContext.getPackageName();
+
+        // before disabling
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
+        assertEquals(DISABLE_NONE,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, user2Id)[0]);
+        // disable
+        mStatusBarManagerService.disableForUser(expectedUser1Flags, mMockStatusBar, pkg, user1Id);
+        mStatusBarManagerService.disableForUser(expectedUser2Flags, mMockStatusBar, pkg, user2Id);
+        // check that right flag is disabled
+        assertEquals(expectedUser1Flags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
+        assertEquals(expectedUser2Flags,
+                mStatusBarManagerService.getDisableFlags(mMockStatusBar, user2Id)[0]);
+    }
+
+
     private void mockUidCheck() {
         mockUidCheck(TEST_PACKAGE);
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
old mode 100755
new mode 100644
index 15c9bfb..398dc281
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -208,6 +208,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.LauncherApps;
 import android.content.pm.ModuleInfo;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
@@ -511,7 +512,8 @@
     TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker;
 
     TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
             1 << 30);
     @Mock
@@ -626,7 +628,7 @@
         when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
         when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer(
                 (Answer<Boolean>) invocation -> {
-                    // TODO: b/317957802 - This is overly broad and basically makes ANY 
+                    // TODO: b/317957802 - This is overly broad and basically makes ANY
                     //  isSameApp() check pass,  requiring Mockito.reset() for meaningful
                     //  tests! Make it more precise.
                     Object[] args = invocation.getArguments();
@@ -6892,22 +6894,15 @@
         verify(visitor, times(1)).accept(eq(personIcon3.getUri()));
     }
 
-    private PendingIntent getPendingIntentWithUri(Uri uri) {
-        return PendingIntent.getActivity(mContext, 0,
-                new Intent("action", uri),
-                PendingIntent.FLAG_IMMUTABLE);
-    }
-
     @Test
-    @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
-    public void testVisitUris_callStyle_ongoingCall() {
+    public void testVisitUris_callStyle() {
         Icon personIcon = Icon.createWithContentUri("content://media/person");
         Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
         Person callingPerson = new Person.Builder().setName("Someone")
                 .setIcon(personIcon)
                 .build();
-        Uri hangUpUri = Uri.parse("content://intent/hangup");
-        PendingIntent hangUpIntent = getPendingIntentWithUri(hangUpUri);
+        PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         Notification n = new Notification.Builder(mContext, "a")
                 .setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent)
                         .setVerificationIcon(verificationIcon))
@@ -6920,42 +6915,10 @@
 
         verify(visitor, times(1)).accept(eq(personIcon.getUri()));
         verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
-        verify(visitor, times(1)).accept(eq(hangUpUri));
         hangUpIntent.cancel();
     }
 
     @Test
-    @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
-    public void testVisitUris_callStyle_incomingCall() {
-        Icon personIcon = Icon.createWithContentUri("content://media/person");
-        Icon verificationIcon = Icon.createWithContentUri("content://media/verification");
-        Person callingPerson = new Person.Builder().setName("Someone")
-                .setIcon(personIcon)
-                .build();
-        Uri answerUri = Uri.parse("content://intent/answer");
-        PendingIntent answerIntent = getPendingIntentWithUri(answerUri);
-        Uri declineUri = Uri.parse("content://intent/decline");
-        PendingIntent declineIntent = getPendingIntentWithUri(declineUri);
-        Notification n = new Notification.Builder(mContext, "a")
-                .setStyle(Notification.CallStyle.forIncomingCall(callingPerson, declineIntent,
-                                answerIntent)
-                        .setVerificationIcon(verificationIcon))
-                .setContentTitle("Calling...")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .build();
-
-        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
-        n.visitUris(visitor);
-
-        verify(visitor, times(1)).accept(eq(personIcon.getUri()));
-        verify(visitor, times(1)).accept(eq(verificationIcon.getUri()));
-        verify(visitor, times(1)).accept(eq(answerIntent.getIntent().getData()));
-        verify(visitor, times(1)).accept(eq(declineUri));
-        answerIntent.cancel();
-        declineIntent.cancel();
-    }
-
-    @Test
     public void testVisitUris_styleExtrasWithoutStyle() {
         Notification.Builder notification = new Notification.Builder(mContext, "a")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
@@ -7001,87 +6964,23 @@
     }
 
     @Test
-    @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
     public void testVisitUris_wearableExtender() {
         Icon actionIcon = Icon.createWithContentUri("content://media/action");
         Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction");
-        Uri displayIntentUri = Uri.parse("content://intent/display");
-        PendingIntent displayIntent = getPendingIntentWithUri(displayIntentUri);
-        Uri actionIntentUri = Uri.parse("content://intent/action");
-        PendingIntent actionIntent = getPendingIntentWithUri(actionIntentUri);
-        Uri wearActionIntentUri = Uri.parse("content://intent/wear");
-        PendingIntent wearActionIntent = getPendingIntentWithUri(wearActionIntentUri);
+        PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(),
+                PendingIntent.FLAG_IMMUTABLE);
         Notification n = new Notification.Builder(mContext, "a")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .addAction(
-                        new Notification.Action.Builder(actionIcon, "Hey!", actionIntent).build())
-                .extend(new Notification.WearableExtender()
-                        .setDisplayIntent(displayIntent)
-                        .addAction(new Notification.Action.Builder(wearActionIcon, "Wear!",
-                                wearActionIntent)
-                                .build()))
+                .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build())
+                .extend(new Notification.WearableExtender().addAction(
+                        new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build()))
                 .build();
 
         Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
         n.visitUris(visitor);
 
         verify(visitor).accept(eq(actionIcon.getUri()));
-        verify(visitor, times(1)).accept(eq(actionIntentUri));
         verify(visitor).accept(eq(wearActionIcon.getUri()));
-        verify(visitor, times(1)).accept(eq(wearActionIntentUri));
-        displayIntent.cancel();
-        actionIntent.cancel();
-        wearActionIntent.cancel();
-    }
-
-    @Test
-    @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
-    public void testVisitUris_tvExtender() {
-        Uri contentIntentUri = Uri.parse("content://intent/content");
-        PendingIntent contentIntent = getPendingIntentWithUri(contentIntentUri);
-        Uri deleteIntentUri = Uri.parse("content://intent/delete");
-        PendingIntent deleteIntent = getPendingIntentWithUri(deleteIntentUri);
-        Notification n = new Notification.Builder(mContext, "a")
-                .extend(
-                        new Notification.TvExtender()
-                                .setContentIntent(contentIntent)
-                                .setDeleteIntent(deleteIntent))
-                .build();
-
-        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
-        n.visitUris(visitor);
-
-        verify(visitor, times(1)).accept(eq(contentIntentUri));
-        verify(visitor, times(1)).accept(eq(deleteIntentUri));
-        contentIntent.cancel();
-        deleteIntent.cancel();
-    }
-
-    @Test
-    @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS})
-    public void testVisitUris_carExtender() {
-        final String testParticipant = "testParticipant";
-        Uri readPendingIntentUri = Uri.parse("content://intent/read");
-        PendingIntent readPendingIntent = getPendingIntentWithUri(readPendingIntentUri);
-        Uri replyPendingIntentUri = Uri.parse("content://intent/reply");
-        PendingIntent replyPendingIntent = getPendingIntentWithUri(replyPendingIntentUri);
-        final RemoteInput testRemoteInput = new RemoteInput.Builder("key").build();
-
-        Notification n = new Notification.Builder(mContext, "a")
-                .extend(new Notification.CarExtender().setUnreadConversation(
-                        new Notification.CarExtender.Builder(testParticipant)
-                                .setReadPendingIntent(readPendingIntent)
-                                .setReplyAction(replyPendingIntent, testRemoteInput)
-                                .build()))
-                .build();
-
-        Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class);
-        n.visitUris(visitor);
-
-        verify(visitor, times(1)).accept(eq(readPendingIntentUri));
-        verify(visitor, times(1)).accept(eq(replyPendingIntentUri));
-        readPendingIntent.cancel();
-        replyPendingIntent.cancel();
     }
 
     @Test
@@ -12174,6 +12073,127 @@
     }
 
     @Test
+    public void testGetPackagesBypassingDnd_empty() throws RemoteException {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true);
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    public void testGetPackagesBypassingDnd_excludeConversationChannels() throws RemoteException {
+        mService.setPreferencesHelper(mPreferencesHelper);
+
+        // Set packages
+        PackageInfo pkg0 = new PackageInfo();
+        pkg0.packageName = "pkg0";
+        pkg0.applicationInfo = new ApplicationInfo();
+        pkg0.applicationInfo.uid = mUid;
+        PackageInfo pkg1 = new PackageInfo();
+        pkg1.packageName = "pkg1";
+        pkg1.applicationInfo = new ApplicationInfo();
+        pkg1.applicationInfo.uid = mUid;
+        PackageInfo pkg2 = new PackageInfo();
+        pkg2.packageName = "pkg2";
+        pkg2.applicationInfo = new ApplicationInfo();
+        pkg2.applicationInfo.uid = mUid;
+
+        when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId))
+                .thenReturn(List.of(pkg0, pkg1, pkg2));
+
+        // Conversation channels
+        NotificationChannel nc0 = new NotificationChannel("id0", "id0",
+                NotificationManager.IMPORTANCE_HIGH);
+        nc0.setConversationId("parentChannel", "conversationId");
+
+        // Demoted conversation channel
+        NotificationChannel nc1 = new NotificationChannel("id1", "id1",
+                NotificationManager.IMPORTANCE_HIGH);
+        nc1.setConversationId("parentChannel", "conversationId");
+        nc1.setDemoted(true);
+
+        // Non-conversation channels
+        NotificationChannel nc2 = new NotificationChannel("id2", "id2",
+                NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel nc3 = new NotificationChannel("id3", "id3",
+                NotificationManager.IMPORTANCE_HIGH);
+
+        ParceledListSlice<NotificationChannel> pls0 =
+                new ParceledListSlice(ImmutableList.of(nc0));
+        ParceledListSlice<NotificationChannel> pls1 =
+                new ParceledListSlice(ImmutableList.of(nc1));
+        ParceledListSlice<NotificationChannel> pls2 =
+                new ParceledListSlice(ImmutableList.of(nc2, nc3));
+
+        when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid))
+                .thenReturn(pls0);
+        when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid))
+                .thenReturn(pls1);
+        when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid))
+                .thenReturn(pls2);
+
+        List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, false);
+
+        assertThat(result).containsExactly("pkg1", "pkg2");
+    }
+
+    @Test
+    public void testGetPackagesBypassingDnd_includeConversationChannels() throws RemoteException {
+        mService.setPreferencesHelper(mPreferencesHelper);
+
+        // Set packages
+        PackageInfo pkg0 = new PackageInfo();
+        pkg0.packageName = "pkg0";
+        pkg0.applicationInfo = new ApplicationInfo();
+        pkg0.applicationInfo.uid = mUid;
+        PackageInfo pkg1 = new PackageInfo();
+        pkg1.packageName = "pkg1";
+        pkg1.applicationInfo = new ApplicationInfo();
+        pkg1.applicationInfo.uid = mUid;
+        PackageInfo pkg2 = new PackageInfo();
+        pkg2.packageName = "pkg2";
+        pkg2.applicationInfo = new ApplicationInfo();
+        pkg2.applicationInfo.uid = mUid;
+
+        when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId))
+                .thenReturn(List.of(pkg0, pkg1, pkg2));
+
+        // Conversation channels
+        NotificationChannel nc0 = new NotificationChannel("id0", "id0",
+                NotificationManager.IMPORTANCE_HIGH);
+        nc0.setConversationId("parentChannel", "conversationId");
+
+        // Demoted conversation channel
+        NotificationChannel nc1 = new NotificationChannel("id1", "id1",
+                NotificationManager.IMPORTANCE_HIGH);
+        nc1.setConversationId("parentChannel", "conversationId");
+        nc1.setDemoted(true);
+
+        // Non-conversation channels
+        NotificationChannel nc2 = new NotificationChannel("id2", "id2",
+                NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel nc3 = new NotificationChannel("id3", "id3",
+                NotificationManager.IMPORTANCE_HIGH);
+
+        ParceledListSlice<NotificationChannel> pls0 =
+                new ParceledListSlice(ImmutableList.of(nc0));
+        ParceledListSlice<NotificationChannel> pls1 =
+                new ParceledListSlice(ImmutableList.of(nc1));
+        ParceledListSlice<NotificationChannel> pls2 =
+                new ParceledListSlice(ImmutableList.of(nc2, nc3));
+
+        when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid))
+                .thenReturn(pls0);
+        when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid))
+                .thenReturn(pls1);
+        when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid))
+                .thenReturn(pls2);
+
+        List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true);
+
+        assertThat(result).containsExactly("pkg0", "pkg1", "pkg2");
+    }
+
+    @Test
     public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception {
         // set the testable NMS to not system uid/appid
         mService.isSystemUid = false;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 863cda4..594d6f2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -34,6 +34,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -87,6 +88,7 @@
 import javax.annotation.Nullable;
 
 @RunWith(AndroidJUnit4.class)
+@EnableFlags(Flags.FLAG_VISIT_PERSON_URI)
 public class NotificationVisitUrisTest extends UiServiceTestCase {
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -153,10 +155,6 @@
                     .put(Notification.Action.Builder.class, "extend")
                     // Overwrites icon supplied to constructor.
                     .put(Notification.BubbleMetadata.Builder.class, "setIcon")
-                    // Overwrites intent supplied to constructor.
-                    .put(Notification.BubbleMetadata.Builder.class, "setIntent")
-                    // Overwrites intent supplied to constructor.
-                    .put(Notification.BubbleMetadata.Builder.class, "setDeleteIntent")
                     // Discards previously-added actions.
                     .put(RemoteViews.class, "mergeRemoteViews")
                     .build();
@@ -172,7 +170,6 @@
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getInstrumentation().getContext();
-        mSetFlagsRule.enableFlags(Flags.FLAG_VISIT_RISKY_URIS);
     }
 
     @After
@@ -700,14 +697,13 @@
             }
 
             if (clazz == Intent.class) {
-                return new Intent("action", generateUri(where.plus(Intent.class)));
+                return new Intent("action");
             }
 
             if (clazz == PendingIntent.class) {
-                // PendingIntent can have an Intent with a Uri.
-                Uri intentUri = generateUri(where.plus(PendingIntent.class));
-                return PendingIntent.getActivity(mContext, 0,
-                        new Intent("action", intentUri),
+                // PendingIntent can have an Intent with a Uri but those are inaccessible and
+                // not inspected.
+                return PendingIntent.getActivity(mContext, 0, new Intent("action"),
                         PendingIntent.FLAG_IMMUTABLE);
             }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index abfb95c..9352c12 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -17,7 +17,16 @@
 package com.android.server.notification;
 
 import static android.app.AutomaticZenRule.TYPE_BEDTIME;
+import static android.app.Flags.FLAG_MODES_UI;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.Condition.SOURCE_USER_ACTION;
+import static android.service.notification.Condition.STATE_FALSE;
+import static android.service.notification.Condition.STATE_TRUE;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
+import static android.service.notification.ZenPolicy.STATE_ALLOW;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -33,6 +42,9 @@
 import android.content.ComponentName;
 import android.net.Uri;
 import android.os.Parcel;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.service.notification.Condition;
@@ -43,7 +55,6 @@
 import android.util.Xml;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
@@ -63,9 +74,13 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.time.Instant;
+import java.util.List;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 public class ZenModeConfigTest extends UiServiceTestCase {
 
     private final String NAME = "name";
@@ -91,6 +106,16 @@
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
             SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(
+                FLAG_MODES_UI);
+    }
+
+    public ZenModeConfigTest(FlagsParameterization flags) {
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public final void setUp() {
         mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
@@ -101,13 +126,44 @@
         ZenModeConfig config = getMutedRingerConfig();
         assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
 
-        config.allowReminders = true;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowReminders(true)
+                    .build();
+        } else {
+            config.setAllowReminders(true);
+        }
         assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
-        config.allowReminders = false;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowReminders(false)
+                    .build();
+        } else {
+            config.setAllowReminders(false);
+        }
+        assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
 
         config.areChannelsBypassingDnd = true;
+        assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowPriorityChannels(true)
+                    .build();
+        } else {
+            config.setAllowPriorityChannels(true);
+        }
+
         assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+
         config.areChannelsBypassingDnd = false;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowPriorityChannels(false)
+                    .build();
+        } else {
+            config.setAllowPriorityChannels(false);
+        }
 
         assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
     }
@@ -122,6 +178,8 @@
     @Test
     public void testZenPolicyToNotificationPolicy_classic() {
         ZenModeConfig config = getMutedAllConfig();
+        // this shouldn't usually be directly set, but since it's a test that involved the default
+        // policy, calling setNotificationPolicy as a precondition may obscure issues
         config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
 
         // Explicitly allow conversations from priority senders to make sure that goes through
@@ -154,8 +212,9 @@
 
     @Test
     public void testZenPolicyToNotificationPolicy() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         ZenModeConfig config = getMutedAllConfig();
+        // this shouldn't usually be directly set, but since it's a test that involved the default
+        // policy, calling setNotificationPolicy as a precondition may obscure issues
         config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
 
         // Explicitly allow conversations from priority senders to make sure that goes through
@@ -194,14 +253,16 @@
 
     @Test
     public void testZenPolicyToNotificationPolicy_unsetChannelsTakesDefault() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         ZenModeConfig config = new ZenModeConfig();
         ZenPolicy zenPolicy = new ZenPolicy.Builder().build();
 
         // When allowChannels is not set to anything in the ZenPolicy builder, make sure it takes
         // the default value from the zen mode config.
         Policy policy = config.toNotificationPolicy(zenPolicy);
-        assertEquals(config.allowPriorityChannels, policy.allowPriorityChannels());
+        assertEquals(Flags.modesUi()
+                ? config.manualRule.zenPolicy.getPriorityChannelsAllowed() == STATE_ALLOW
+                : config.isAllowPriorityChannels(),
+                policy.allowPriorityChannels());
     }
 
     @Test
@@ -219,18 +280,22 @@
                 .build();
 
         ZenModeConfig config = getMutedAllConfig();
-        config.allowAlarms = true;
-        config.allowReminders = true;
-        config.allowEvents = true;
-        config.allowCalls = true;
-        config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS;
-        config.allowMessages = true;
-        config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED;
-        config.allowConversations = false;
-        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
-        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
-        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
-        ZenPolicy actual = config.toZenPolicy();
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = expected.copy();
+        } else {
+            config.setAllowAlarms(true);
+            config.setAllowReminders(true);
+            config.setAllowEvents(true);
+            config.setAllowCalls(true);
+            config.setAllowCallsFrom(Policy.PRIORITY_SENDERS_CONTACTS);
+            config.setAllowMessages(true);
+            config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED);
+            config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
+            config.setSuppressedVisualEffects(config.getSuppressedVisualEffects()
+                    | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS
+                    | Policy.SUPPRESSED_EFFECT_AMBIENT);
+        }
+        ZenPolicy actual = config.getZenPolicy();
 
         assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge());
         assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms());
@@ -247,7 +312,6 @@
 
     @Test
     public void testZenConfigToZenPolicy() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         ZenPolicy expected = new ZenPolicy.Builder()
                 .allowAlarms(true)
                 .allowReminders(true)
@@ -262,19 +326,24 @@
                 .build();
 
         ZenModeConfig config = getMutedAllConfig();
-        config.allowAlarms = true;
-        config.allowReminders = true;
-        config.allowEvents = true;
-        config.allowCalls = true;
-        config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS;
-        config.allowMessages = true;
-        config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED;
-        config.allowConversations = false;
-        config.allowPriorityChannels = false;
-        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
-        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
-        config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
-        ZenPolicy actual = config.toZenPolicy();
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = expected.copy();
+        } else {
+            config.setAllowAlarms(true);
+            config.setAllowReminders(true);
+
+            config.setAllowEvents(true);
+            config.setAllowCalls(true);
+            config.setAllowCallsFrom(Policy.PRIORITY_SENDERS_CONTACTS);
+            config.setAllowMessages(true);
+            config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED);
+            config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
+            config.setAllowPriorityChannels(false);
+            config.setSuppressedVisualEffects(config.getSuppressedVisualEffects()
+                    | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS
+                    | Policy.SUPPRESSED_EFFECT_AMBIENT);
+        }
+        ZenPolicy actual = config.getZenPolicy();
 
         assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge());
         assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms());
@@ -296,20 +365,64 @@
         assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
         assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
 
-        config.allowReminders = true;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowReminders(true)
+                    .build();
+        } else {
+            config.setAllowReminders(true);
+        }
         assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
         assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
-        config.allowReminders = false;
+
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowReminders(false)
+                    .build();
+        } else {
+            config.setAllowReminders(false);
+        }
+
+        assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
+        assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
 
         config.areChannelsBypassingDnd = true;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowPriorityChannels(true)
+                    .build();
+        } else {
+            config.setAllowPriorityChannels(true);
+        }
+
         assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
         assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
-        config.areChannelsBypassingDnd = false;
 
-        config.allowAlarms = true;
+        config.areChannelsBypassingDnd = false;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowPriorityChannels(false)
+                    .build();
+        } else {
+            config.setAllowPriorityChannels(false);
+        }
+
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowAlarms(true)
+                    .build();
+        } else {
+            config.setAllowAlarms(true);
+        }
         assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
         assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
-        config.allowAlarms = false;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy)
+                    .allowAlarms(false)
+                    .build();
+        } else {
+            config.setAllowAlarms(false);
+        }
 
         assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config));
         assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config));
@@ -375,8 +488,6 @@
 
     @Test
     public void testWriteToParcel() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
         rule.configurationActivity = CONFIG_ACTIVITY;
         rule.component = OWNER;
@@ -473,8 +584,6 @@
 
     @Test
     public void testRuleXml() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
         ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
         rule.configurationActivity = CONFIG_ACTIVITY;
         rule.component = OWNER;
@@ -719,8 +828,6 @@
 
     @Test
     public void testZenPolicyXml() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
         ZenPolicy policy = new ZenPolicy.Builder()
                 .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS)
                 .allowMessages(ZenPolicy.PEOPLE_TYPE_NONE)
@@ -770,63 +877,173 @@
                 fromXml.getVisualEffectNotificationList());
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    public void testisManualActive_stateTrue() {
+        ZenModeConfig config = getMutedAllConfig();
+        final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule();
+        newRule.type = AutomaticZenRule.TYPE_OTHER;
+        newRule.enabled = true;
+        newRule.conditionId = Uri.EMPTY;
+        newRule.allowManualInvocation = true;
+        config.manualRule = newRule;
+        config.manualRule.pkg = "android";
+        config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_TRUE, SOURCE_USER_ACTION);
+
+        assertThat(config.isManualActive()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    public void testisManualActive_stateFalse() {
+        ZenModeConfig config = getMutedAllConfig();
+        final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule();
+        newRule.type = AutomaticZenRule.TYPE_OTHER;
+        newRule.enabled = true;
+        newRule.conditionId = Uri.EMPTY;
+        newRule.allowManualInvocation = true;
+        config.manualRule = newRule;
+        config.manualRule.pkg = "android";
+        config.manualRule.zenMode = ZEN_MODE_OFF;
+        config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_FALSE, SOURCE_USER_ACTION);
+
+        assertThat(config.isManualActive()).isFalse();
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MODES_UI)
+    public void testisManualActive_noState() {
+        ZenModeConfig config = getMutedAllConfig();
+        final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule();
+        newRule.type = AutomaticZenRule.TYPE_OTHER;
+        newRule.enabled = true;
+        newRule.conditionId = Uri.EMPTY;
+        newRule.allowManualInvocation = true;
+        config.manualRule = newRule;
+        config.manualRule.pkg = "android";
+        config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+
+        assertThat(config.isManualActive()).isTrue();
+    }
+
+    @Test
+    public void testisManualActive_noRule() {
+        ZenModeConfig config = getMutedAllConfig();
+
+        assertThat(config.isManualActive()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    public void testRuleXml_manual_upgrade() throws Exception {
+        ZenModeConfig config = getMutedAllConfig();
+        final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule();
+        newRule.type = AutomaticZenRule.TYPE_OTHER;
+        newRule.enabled = true;
+        newRule.conditionId = Uri.EMPTY;
+        newRule.allowManualInvocation = true;
+        newRule.pkg = "android";
+        newRule.zenMode = ZEN_MODE_OFF;
+        config.manualRule = newRule;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeRuleXml(newRule, baos);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+        assertThat(fromXml.zenPolicy).isEqualTo(config.getZenPolicy());
+    }
+
     private ZenModeConfig getMutedRingerConfig() {
         ZenModeConfig config = new ZenModeConfig();
-        // Allow alarms, media
-        config.allowAlarms = true;
-        config.allowMedia = true;
 
-        // All sounds that respect the ringer are not allowed
-        config.allowSystem = false;
-        config.allowCalls = false;
-        config.allowRepeatCallers = false;
-        config.allowMessages = false;
-        config.allowReminders = false;
-        config.allowEvents = false;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder()
+                    .disallowAllSounds()
+                    .allowAlarms(true)
+                    .allowMedia(true)
+                    .allowPriorityChannels(false)
+                    .showAllVisualEffects()
+                    .build();
+        } else {
+            // Allow alarms, media
+            config.setAllowAlarms(true);
+            config.setAllowMedia(true);
+
+            // All sounds that respect the ringer are not allowed
+            config.setAllowSystem(false);
+            config.setAllowCalls(false);
+            config.setAllowRepeatCallers(false);
+            config.setAllowMessages(false);
+            config.setAllowReminders(false);
+            config.setAllowEvents(false);
+            config.setSuppressedVisualEffects(0);
+            config.setAllowPriorityChannels(false);
+        }
         config.areChannelsBypassingDnd = false;
 
-        config.suppressedVisualEffects = 0;
-
         return config;
     }
 
     private ZenModeConfig getCustomConfig() {
         ZenModeConfig config = new ZenModeConfig();
-        // Some sounds allowed
-        config.allowAlarms = true;
-        config.allowMedia = false;
-        config.allowSystem = false;
-        config.allowCalls = true;
-        config.allowRepeatCallers = true;
-        config.allowMessages = false;
-        config.allowReminders = false;
-        config.allowEvents = false;
-        config.areChannelsBypassingDnd = false;
-        config.allowCallsFrom = ZenModeConfig.SOURCE_ANYONE;
-        config.allowMessagesFrom = ZenModeConfig.SOURCE_ANYONE;
-        config.allowConversations = true;
-        config.allowConversationsFrom = CONVERSATION_SENDERS_IMPORTANT;
 
-        config.suppressedVisualEffects = 0;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder()
+                    .disallowAllSounds()
+                    .allowAlarms(true)
+                    .allowCalls(PEOPLE_TYPE_ANYONE)
+                    .allowRepeatCallers(true)
+                    .allowConversations(CONVERSATION_SENDERS_IMPORTANT)
+                    .allowPriorityChannels(true)
+                    .showAllVisualEffects()
+                    .build();
+        } else {
+            // Some sounds allowed
+            config.setAllowAlarms(true);
+            config.setAllowMedia(false);
+            config.setAllowSystem(false);
+            config.setAllowCalls(true);
+            config.setAllowRepeatCallers(true);
+            config.setAllowMessages(false);
+            config.setAllowReminders(false);
+            config.setAllowEvents(false);
+            config.setAllowCallsFrom(ZenModeConfig.SOURCE_ANYONE);
+            config.setAllowMessagesFrom(ZenModeConfig.SOURCE_ANYONE);
+            config.setAllowConversations(true);
+            config.setAllowConversationsFrom(CONVERSATION_SENDERS_IMPORTANT);
+            config.setSuppressedVisualEffects(0);
+            config.setAllowPriorityChannels(true);
+        }
+        config.areChannelsBypassingDnd = false;
         return config;
     }
 
     private ZenModeConfig getMutedAllConfig() {
         ZenModeConfig config = new ZenModeConfig();
-        // No sounds allowed
-        config.allowAlarms = false;
-        config.allowMedia = false;
-        config.allowSystem = false;
-        config.allowCalls = false;
-        config.allowRepeatCallers = false;
-        config.allowMessages = false;
-        config.allowReminders = false;
-        config.allowEvents = false;
-        config.areChannelsBypassingDnd = false;
-        config.allowConversations = false;
-        config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE;
 
-        config.suppressedVisualEffects = 0;
+        if (Flags.modesUi()) {
+            config.manualRule.zenPolicy = new ZenPolicy.Builder()
+                    .disallowAllSounds()
+                    .showAllVisualEffects()
+                    .allowPriorityChannels(false)
+                    .build();
+        } else {
+            // No sounds allowed
+            config.setAllowAlarms(false);
+            config.setAllowMedia(false);
+            config.setAllowSystem(false);
+            config.setAllowCalls(false);
+            config.setAllowRepeatCallers(false);
+            config.setAllowMessages(false);
+            config.setAllowReminders(false);
+            config.setAllowEvents(false);
+            config.setAllowConversations(false);
+            config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE);
+            config.setSuppressedVisualEffects(0);
+        }
+        config.areChannelsBypassingDnd = false;
         return config;
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 2e64645..26a13cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import static android.app.Flags.FLAG_MODES_UI;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertEquals;
@@ -27,8 +29,10 @@
 
 import android.app.AutomaticZenRule;
 import android.app.Flags;
+import android.app.NotificationManager;
 import android.content.ComponentName;
 import android.net.Uri;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.service.notification.Condition;
@@ -37,7 +41,6 @@
 import android.service.notification.ZenModeDiff;
 import android.service.notification.ZenModeDiff.RuleDiff;
 import android.service.notification.ZenPolicy;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArrayMap;
 
@@ -60,8 +63,11 @@
 import java.util.Optional;
 import java.util.Set;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper
 public class ZenModeDiffTest extends UiServiceTestCase {
     // Base set of exempt fields independent of fields that are enabled/disabled via flags.
@@ -91,6 +97,16 @@
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(
+                FLAG_MODES_UI);
+    }
+
+    public ZenModeDiffTest(FlagsParameterization flags) {
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Test
     public void testRuleDiff_addRemoveSame() {
         // Test add, remove, and both sides same
@@ -220,21 +236,35 @@
         ZenModeConfig c2 = new ZenModeConfig();
 
         // set c1 and c2 to have some different senders
-        c1.allowMessagesFrom = ZenModeConfig.SOURCE_STAR;
-        c2.allowMessagesFrom = ZenModeConfig.SOURCE_CONTACT;
-        c1.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
-        c2.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE;
+        NotificationManager.Policy c1Policy = c1.toNotificationPolicy();
+        c1.applyNotificationPolicy(new NotificationManager.Policy(
+                c1Policy.priorityCategories, c1Policy.priorityCallSenders,
+                c1Policy.PRIORITY_SENDERS_STARRED, c1Policy.suppressedVisualEffects,
+                c1Policy.state, ZenPolicy.CONVERSATION_SENDERS_IMPORTANT));
+
+        NotificationManager.Policy c2Policy = c1.toNotificationPolicy();
+        c2.applyNotificationPolicy(new NotificationManager.Policy(
+                c2Policy.priorityCategories, c2Policy.priorityCallSenders,
+                c2Policy.PRIORITY_SENDERS_CONTACTS, c2Policy.suppressedVisualEffects,
+                c2Policy.state, ZenPolicy.CONVERSATION_SENDERS_NONE));
 
         ZenModeDiff.ConfigDiff d = new ZenModeDiff.ConfigDiff(c1, c2);
         assertTrue(d.hasDiff());
 
-        // Diff in top-level fields
-        assertTrue(d.getDiffForField("allowMessagesFrom").hasDiff());
-        assertTrue(d.getDiffForField("allowConversationsFrom").hasDiff());
+        if (!Flags.modesUi()) {
+            // Diff in top-level fields
+            assertTrue(d.getDiffForField("allowMessagesFrom").hasDiff());
+            assertTrue(d.getDiffForField("allowConversationsFrom").hasDiff());
 
-        // Bonus testing of stringification of people senders and conversation senders
-        assertTrue(d.toString().contains("allowMessagesFrom:stars->contacts"));
-        assertTrue(d.toString().contains("allowConversationsFrom:important->none"));
+            // Bonus testing of stringification of people senders and conversation senders
+            assertTrue(d.toString().contains("allowMessagesFrom:stars->contacts"));
+            assertTrue(d.toString().contains("allowConversationsFrom:important->none"));
+        } else {
+            RuleDiff r = d.getManualRuleDiff();
+            assertNotNull(r);
+            ZenModeDiff.FieldDiff p = r.getDiffForField(RuleDiff.FIELD_ZEN_POLICY);
+            assertNotNull(p);
+        }
     }
 
     @Test
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 72ace84..201b286 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.AutomaticZenRule.TYPE_BEDTIME;
 import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
+import static android.app.Flags.FLAG_MODES_API;
+import static android.app.Flags.FLAG_MODES_UI;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED;
@@ -41,11 +43,9 @@
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND;
+import static android.app.NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
@@ -64,6 +64,11 @@
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
 
 import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
 import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
@@ -75,6 +80,7 @@
 
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
@@ -131,6 +137,7 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.provider.Settings.Global;
@@ -140,7 +147,6 @@
 import android.service.notification.ZenAdapters;
 import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.service.notification.ZenModeConfig.ZenRule;
 import android.service.notification.ZenModeDiff;
@@ -172,8 +178,6 @@
 import com.google.common.truth.Correspondence;
 import com.google.common.util.concurrent.SettableFuture;
 import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.testing.junit.testparameterinjector.TestParameter;
-import com.google.testing.junit.testparameterinjector.TestParameterInjector;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -202,9 +206,12 @@
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
-@RunWith(TestParameterInjector.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper
 public class ZenModeHelperTest extends UiServiceTestCase {
 
@@ -268,6 +275,16 @@
     ZenModeEventLoggerFake mZenModeEventLogger;
     private String mPkg;
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.progressionOf(FLAG_MODES_API,
+                FLAG_MODES_UI);
+    }
+
+    public ZenModeHelperTest(FlagsParameterization flags) {
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setUp() throws PackageManager.NameNotFoundException {
         MockitoAnnotations.initMocks(this);
@@ -677,7 +694,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testTotalSilence_consolidatedPolicyDisallowsAll() {
         // Start with zen mode off just to make sure global/manual mode isn't doing anything.
         mZenModeHelper.mZenMode = ZEN_MODE_OFF;
@@ -711,7 +728,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testAlarmsOnly_consolidatedPolicyOnlyAllowsAlarmsAndMedia() {
         // Start with zen mode off just to make sure global/manual mode isn't doing anything.
         mZenModeHelper.mZenMode = ZEN_MODE_OFF;
@@ -802,15 +819,11 @@
         // 1. Current ringer is normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         // Set zen to priority-only with all notification sounds muted (so ringer will be muted)
+        Policy totalSilence = new Policy(0,0,0);
+        mZenModeHelper.setNotificationPolicy(totalSilence, UPDATE_ORIGIN_APP, 1);
         mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelper.mConfig.allowReminders = false;
-        mZenModeHelper.mConfig.allowCalls = false;
-        mZenModeHelper.mConfig.allowMessages = false;
-        mZenModeHelper.mConfig.allowEvents = false;
-        mZenModeHelper.mConfig.allowRepeatCallers = false;
-        mZenModeHelper.mConfig.allowConversations = false;
 
-        // 2. apply priority only zen - verify ringer is unchanged
+        // 2. verify ringer is unchanged
         mZenModeHelper.applyZenToRingerMode();
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
                 mZenModeHelper.TAG);
@@ -844,9 +857,8 @@
     public void testRingerAffectedStreamsPriorityOnly() {
         // in priority only mode:
         // ringtone, notification and system streams are affected by ringer mode
-        mZenModeHelper.mConfig.allowAlarms = true;
-        mZenModeHelper.mConfig.allowReminders = true;
-        mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
+                UPDATE_ORIGIN_APP, "test", "caller", 1);
         ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerMuted =
                 mZenModeHelper.new RingerModeDelegate();
 
@@ -862,13 +874,9 @@
 
         // even when ringer is muted (since all ringer sounds cannot bypass DND),
         // system stream is still affected by ringer mode
-        mZenModeHelper.mConfig.allowSystem = false;
-        mZenModeHelper.mConfig.allowReminders = false;
-        mZenModeHelper.mConfig.allowCalls = false;
-        mZenModeHelper.mConfig.allowMessages = false;
-        mZenModeHelper.mConfig.allowEvents = false;
-        mZenModeHelper.mConfig.allowRepeatCallers = false;
-        mZenModeHelper.mConfig.allowConversations = false;
+        mZenModeHelper.setNotificationPolicy(new Policy(0,0,0), UPDATE_ORIGIN_APP, 1);
+        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
+                UPDATE_ORIGIN_APP, "test", "caller", 1);
         ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerNotMuted =
                 mZenModeHelper.new RingerModeDelegate();
 
@@ -885,7 +893,7 @@
     }
 
     @Test
-    public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_StartNormal() {
+    public void applyZenToRingerMode_ZEN_MODE_IMPORTANT_INTERRUPTIONS() {
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelper.mAudioManager = mAudioManager;
         Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL,
@@ -894,7 +902,6 @@
         // 1. Current ringer is normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelper.mConfig.allowReminders = true;
 
         // 2. apply priority only zen - verify ringer is normal
         mZenModeHelper.applyZenToRingerMode();
@@ -919,7 +926,6 @@
         // 1. Current ringer is silent
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelper.mConfig.allowReminders = true;
 
         // 2. apply priority only zen - verify ringer is silent
         mZenModeHelper.applyZenToRingerMode();
@@ -945,7 +951,6 @@
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         // Set zen to priority-only with all notification sounds muted (so ringer will be muted)
         mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelper.mConfig.allowReminders = true;
 
         // 2. apply priority only zen - verify zen will still be normal
         mZenModeHelper.applyZenToRingerMode();
@@ -977,11 +982,9 @@
 
         // apply zen off multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
-        mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
         for (int i = 0; i < 3; i++) {
-            // if zen doesn't change, zen should not reapply itself to the ringer
-            mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
+            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+                    UPDATE_ORIGIN_APP, "test", "caller", 1);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
                 mZenModeHelper.TAG);
@@ -991,8 +994,6 @@
     public void testSilentRingerSavedOnZenOff_startsZenOn() {
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelper.mAudioManager = mAudioManager;
-        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
-        mZenModeHelper.mConfig = new ZenModeConfig();
 
         // previously set silent ringer
         ZenModeHelper.RingerModeDelegate ringerModeDelegate =
@@ -1003,12 +1004,12 @@
         assertEquals(AudioManager.RINGER_MODE_SILENT, Global.getInt(mContext.getContentResolver(),
                 Global.ZEN_MODE_RINGER_LEVEL, AudioManager.RINGER_MODE_NORMAL));
 
-        // apply zen off multiple times - verify ringer is not set to normal
+        // apply zen on multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
-        mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
-            mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
+            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+                    UPDATE_ORIGIN_APP, "test", "caller", 1);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
                 mZenModeHelper.TAG);
@@ -1018,8 +1019,6 @@
     public void testVibrateRingerSavedOnZenOff_startsZenOn() {
         AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class);
         mZenModeHelper.mAudioManager = mAudioManager;
-        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
-        mZenModeHelper.mConfig = new ZenModeConfig();
 
         // previously set silent ringer
         ZenModeHelper.RingerModeDelegate ringerModeDelegate =
@@ -1032,10 +1031,10 @@
 
         // apply zen off multiple times - verify ringer is not set to normal
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
-        mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
-            mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true);
+            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY,
+                    UPDATE_ORIGIN_APP, "test", "caller", 1);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
                 mZenModeHelper.TAG);
@@ -1066,22 +1065,18 @@
 
     @Test
     public void testParcelConfig() {
-        mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelper.mConfig.allowAlarms = false;
-        mZenModeHelper.mConfig.allowMedia = false;
-        mZenModeHelper.mConfig.allowSystem = false;
-        mZenModeHelper.mConfig.allowReminders = true;
-        mZenModeHelper.mConfig.allowCalls = true;
-        mZenModeHelper.mConfig.allowMessages = true;
-        mZenModeHelper.mConfig.allowEvents = true;
-        mZenModeHelper.mConfig.allowRepeatCallers = true;
-        mZenModeHelper.mConfig.allowConversations = true;
-        mZenModeHelper.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
-        mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
-        mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
-        mZenModeHelper.mConfig.manualRule.component = new ComponentName("a", "a");
-        mZenModeHelper.mConfig.manualRule.enabled = true;
-        mZenModeHelper.mConfig.manualRule.snoozing = true;
+        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
+                | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
+                | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
+                PRIORITY_SENDERS_STARRED, 0, CONVERSATION_SENDERS_ANYONE), UPDATE_ORIGIN_UNKNOWN,
+                1);
+        mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
+                .setShouldDimWallpaper(true)
+                .setShouldDisplayGrayscale(true)
+                .setShouldUseNightMode(true)
+                .build(), UPDATE_ORIGIN_UNKNOWN, "test", 1);
+        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
+                UPDATE_ORIGIN_UNKNOWN, "test", "me", 1);
 
         ZenModeConfig actual = mZenModeHelper.mConfig.copy();
 
@@ -1090,24 +1085,17 @@
 
     @Test
     public void testWriteXml() throws Exception {
-        mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelper.mConfig.allowAlarms = false;
-        mZenModeHelper.mConfig.allowMedia = false;
-        mZenModeHelper.mConfig.allowSystem = false;
-        mZenModeHelper.mConfig.allowReminders = true;
-        mZenModeHelper.mConfig.allowCalls = true;
-        mZenModeHelper.mConfig.allowMessages = true;
-        mZenModeHelper.mConfig.allowEvents = true;
-        mZenModeHelper.mConfig.allowRepeatCallers = true;
-        mZenModeHelper.mConfig.allowConversations = true;
-        mZenModeHelper.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE;
-        mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
-        mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
-        mZenModeHelper.mConfig.manualRule.zenMode =
-                ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelper.mConfig.manualRule.component = new ComponentName("a", "a");
-        mZenModeHelper.mConfig.manualRule.pkg = "a";
-        mZenModeHelper.mConfig.manualRule.enabled = true;
+        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS
+                | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS
+                | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED,
+                PRIORITY_SENDERS_STARRED, SUPPRESSED_EFFECT_BADGE, CONVERSATION_SENDERS_ANYONE),
+                UPDATE_ORIGIN_UNKNOWN, 1);
+        mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder()
+                        .setShouldDimWallpaper(true)
+                        .setShouldDisplayGrayscale(true)
+                .build(), UPDATE_ORIGIN_UNKNOWN, "test", 1);
+        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY,
+                UPDATE_ORIGIN_UNKNOWN, "test", "me", 1);
 
         ZenModeConfig expected = mZenModeHelper.mConfig.copy();
         if (Flags.modesUi()) {
@@ -1127,10 +1115,10 @@
 
     @Test
     public void testProto() throws InvalidProtocolBufferException {
-        mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        // existence of manual rule means it should be in output
-        mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
-        mZenModeHelper.mConfig.manualRule.pkg = "android";  // system
+        mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
+                Flags.modesApi() ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
+                null, "test", CUSTOM_PKG_UID);
+
         mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // no automatic rules
 
         List<String> ids = new ArrayList<>();
@@ -1151,7 +1139,6 @@
                 assertTrue(cfg.getEnabled());
                 assertFalse(cfg.getChannelsBypassing());
             }
-            assertEquals(Process.SYSTEM_UID, cfg.getUid());
             String name = cfg.getId();
             assertTrue("unexpected rule id", ids.contains(name));
             ids.remove(name);
@@ -1255,10 +1242,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testProtoWithAutoRuleCustomPolicy() throws Exception {
-        // allowChannels is only valid under modes_api.
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
         setupZenConfig();
         // clear any automatic rules just to make sure
         mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
@@ -1299,7 +1284,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testProtoWithAutoRuleWithModifiedFields() throws Exception {
         setupZenConfig();
         mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
@@ -1384,8 +1369,8 @@
     public void testProtoWithManualRule() throws Exception {
         setupZenConfig();
         mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules();
-        mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
-        mZenModeHelper.mConfig.manualRule.enabled = true;
+        mZenModeHelper.setManualZenMode(INTERRUPTION_FILTER_PRIORITY, Uri.EMPTY, UPDATE_ORIGIN_APP,
+                "test", "me", 1);
 
         List<StatsEvent> events = new LinkedList<>();
         mZenModeHelper.pullRules(events);
@@ -1408,15 +1393,15 @@
         // Setup configs for user 10 and 11.
         setupZenConfig();
         ZenModeConfig config10 = mZenModeHelper.mConfig.copy();
+        Policy policy = new Policy(PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_ALARMS, 0, 0);
+        config10.applyNotificationPolicy(policy);
         config10.user = 10;
-        config10.allowAlarms = true;
-        config10.allowMedia = true;
         mZenModeHelper.setConfig(config10, null, UPDATE_ORIGIN_INIT, "writeXml",
                 Process.SYSTEM_UID);
         ZenModeConfig config11 = mZenModeHelper.mConfig.copy();
         config11.user = 11;
-        config11.allowAlarms = false;
-        config11.allowMedia = false;
+        policy = new Policy(0, 0, 0);
+        config11.applyNotificationPolicy(policy);
         mZenModeHelper.setConfig(config11, null, UPDATE_ORIGIN_INIT, "writeXml",
                 Process.SYSTEM_UID);
 
@@ -1583,8 +1568,9 @@
     }
 
     @Test
-    public void testReadXmlRulesNotOverriden() throws Exception {
+    public void testReadXmlRulesNotOverridden() throws Exception {
         setupZenConfig();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
 
         // automatic zen rule is enabled on upgrade so rules should not be overriden to default
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
@@ -1607,7 +1593,7 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         assertTrue(mZenModeHelper.mConfig.automaticRules.containsKey("customRule"));
-        setupZenConfigMaintained();
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
     }
 
     @Test
@@ -1626,7 +1612,7 @@
         parser.nextTag();
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
-        assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects);
+        assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
 
         xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
@@ -1642,7 +1628,7 @@
         parser.nextTag();
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
-        assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects);
+        assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
     }
 
     @Test
@@ -1661,7 +1647,7 @@
         parser.nextTag();
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
-        assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects);
+        assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects());
     }
 
     @Test
@@ -1680,11 +1666,16 @@
         parser.nextTag();
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
-        assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
-                        | SUPPRESSED_EFFECT_LIGHTS
-                        | SUPPRESSED_EFFECT_AMBIENT
-                        | SUPPRESSED_EFFECT_PEEK,
-                mZenModeHelper.mConfig.suppressedVisualEffects);
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse();
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_LIGHTS, true)).isFalse();
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse();
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isFalse();
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_BADGE, true)).isTrue();
 
         xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
@@ -1700,7 +1691,10 @@
         parser.nextTag();
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
-        assertEquals(SUPPRESSED_EFFECT_PEEK, mZenModeHelper.mConfig.suppressedVisualEffects);
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse();
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isTrue();
 
         xml = "<zen version=\"6\" user=\"0\">\n"
                 + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" "
@@ -1716,18 +1710,23 @@
         parser.nextTag();
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
-        assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
-                        | SUPPRESSED_EFFECT_LIGHTS
-                        | SUPPRESSED_EFFECT_AMBIENT,
-                mZenModeHelper.mConfig.suppressedVisualEffects);
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse();
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_LIGHTS, true)).isFalse();
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isFalse();
+        assertThat(mZenModeHelper.mConfig.getZenPolicy()
+                .isVisualEffectAllowed(VISUAL_EFFECT_BADGE, true)).isTrue();
     }
 
     @Test
     public void testReadXmlResetDefaultRules() throws Exception {
         setupZenConfig();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
 
         // no enabled automatic zen rules and no default rules
-        // so rules should be overriden by default rules
+        // so rules should be overridden by default rules
         mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
 
         // set previous version
@@ -1745,13 +1744,14 @@
             assertTrue(rules.containsKey(defaultId));
         }
 
-        setupZenConfigMaintained();
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
     }
 
 
     @Test
     public void testReadXmlAllDisabledRulesResetDefaultRules() throws Exception {
         setupZenConfig();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
 
         // all automatic zen rules are disabled on upgrade (and default rules don't already exist)
         // so rules should be overriden by default rules
@@ -1782,12 +1782,13 @@
         }
         assertFalse(rules.containsKey("customRule"));
 
-        setupZenConfigMaintained();
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
     }
 
     @Test
     public void testReadXmlOnlyOneDefaultRuleExists() throws Exception {
         setupZenConfig();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
 
         // all automatic zen rules are disabled on upgrade and only one default rule exists
         // so rules should be overriden to the default rules
@@ -1834,12 +1835,13 @@
         }
         assertFalse(rules.containsKey("customRule"));
 
-        setupZenConfigMaintained();
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
     }
 
     @Test
     public void testReadXmlDefaultRulesExist() throws Exception {
         setupZenConfig();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
 
         // Default rules exist so rules should not be overridden by defaults
         ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
@@ -1897,13 +1899,13 @@
 
         // check default rules
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
-        assertTrue(rules.size() != 0);
+        assertEquals(3, rules.size());
         for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) {
             assertTrue(rules.containsKey(defaultId));
         }
         assertTrue(rules.containsKey("customRule"));
 
-        setupZenConfigMaintained();
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
 
         List<StatsEvent> events = new LinkedList<>();
         mZenModeHelper.pullRules(events);
@@ -1911,11 +1913,12 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testReadXml_onModesApi_noUpgrade() throws Exception {
         // When reading XML for something that is already on the modes API system, make sure no
         // rules' policies get changed.
         setupZenConfig();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
 
         // Shared for rules
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRules = new ArrayMap<>();
@@ -1947,7 +1950,7 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         // basic check: global config maintained
-        setupZenConfigMaintained();
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
 
         // Find our automatic rules.
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -1958,12 +1961,13 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testReadXml_upgradeToModesApi_makesCustomPolicies() throws Exception {
         // When reading in an XML file written from a pre-modes-API version, confirm that we create
         // a custom policy matching the global config for any automatic rule with no specified
         // policy.
         setupZenConfig();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
 
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
         ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
@@ -1985,7 +1989,7 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         // basic check: global config maintained
-        setupZenConfigMaintained();
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
 
         // Find our automatic rule and check that it has a policy set now
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -2009,12 +2013,13 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testReadXml_upgradeToModesApi_fillsInCustomPolicies() throws Exception {
         // When reading in an XML file written from a pre-modes-API version, confirm that for an
         // underspecified ZenPolicy, we fill in all of the gaps with things from the global config
         // in order to maintain consistency of behavior.
         setupZenConfig();
+        Policy originalPolicy = mZenModeHelper.getNotificationPolicy();
 
         ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>();
         ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
@@ -2041,7 +2046,7 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
 
         // basic check: global config maintained
-        setupZenConfigMaintained();
+        assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy());
 
         // Find our automatic rule and check that it has a policy set now
         ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules;
@@ -2068,7 +2073,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testReadXml_upgradeToModesApi_existingDefaultRulesGetCustomPolicy()
             throws Exception {
         setupZenConfig();
@@ -2230,7 +2235,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testDefaultRulesFromConfig_modesApi_getPolicies() {
         // After mZenModeHelper was created, set some things in the policy so it's changed from
         // default.
@@ -2389,7 +2394,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testAddAutomaticZenRule_modesApi_fillsInDefaultValues() {
         // When a new automatic zen rule is added with only some fields filled in, ensure that
         // all unset fields are filled in with device defaults.
@@ -2436,19 +2441,19 @@
         assertThat(rule2InConfig.zenPolicy.getPriorityMessageSenders())
                 .isEqualTo(PEOPLE_TYPE_CONTACTS);
         assertThat(rule2InConfig.zenPolicy.getVisualEffectFullScreenIntent())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);
+                .isEqualTo(STATE_ALLOW);
 
         // the rest of rule 2's settings should be the device defaults
         assertThat(rule2InConfig.zenPolicy.getPriorityConversationSenders())
                 .isEqualTo(CONVERSATION_SENDERS_IMPORTANT);
         assertThat(rule2InConfig.zenPolicy.getPriorityCategorySystem())
-                .isEqualTo(ZenPolicy.STATE_DISALLOW);
+                .isEqualTo(STATE_DISALLOW);
         assertThat(rule2InConfig.zenPolicy.getPriorityCategoryAlarms())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);
+                .isEqualTo(STATE_ALLOW);
         assertThat(rule2InConfig.zenPolicy.getVisualEffectPeek())
-                .isEqualTo(ZenPolicy.STATE_DISALLOW);
+                .isEqualTo(STATE_DISALLOW);
         assertThat(rule2InConfig.zenPolicy.getVisualEffectNotificationList())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);
+                .isEqualTo(STATE_ALLOW);
     }
 
     @Test
@@ -2590,9 +2595,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
         ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
                 .setShouldDisplayGrayscale(true)
                 .setShouldSuppressAmbientDisplay(true)
@@ -2624,9 +2628,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void addAutomaticZenRule_fromSystem_respectsHiddenEffects() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
         ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
                 .setShouldDisplayGrayscale(true)
                 .setShouldSuppressAmbientDisplay(true)
@@ -2652,9 +2655,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void addAutomaticZenRule_fromUser_respectsHiddenEffects() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-
         ZenDeviceEffects zde = new ZenDeviceEffects.Builder()
                 .setShouldDisplayGrayscale(true)
                 .setShouldSuppressAmbientDisplay(true)
@@ -2682,8 +2684,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_fromApp_preservesPreviousHiddenEffects() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         ZenDeviceEffects original = new ZenDeviceEffects.Builder()
                 .setShouldDisableTapToWake(true)
                 .addExtraEffect("extra")
@@ -2717,8 +2719,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_fromSystem_updatesHiddenEffects() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         ZenDeviceEffects original = new ZenDeviceEffects.Builder()
                 .setShouldDisableTapToWake(true)
                 .build();
@@ -2744,8 +2746,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_fromUser_updatesHiddenEffects() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         ZenDeviceEffects original = new ZenDeviceEffects.Builder()
                 .setShouldDisableTapToWake(true)
                 .build();
@@ -2775,7 +2777,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_nullPolicy_doesNothing() {
         // Test that when updateAutomaticZenRule is called with a null policy, nothing changes
         // about the existing policy.
@@ -2796,11 +2798,11 @@
 
         AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
         assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
-                .isEqualTo(ZenPolicy.STATE_DISALLOW);
+                .isEqualTo(STATE_DISALLOW);
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_overwritesExistingPolicy() {
         // Test that when updating an automatic zen rule with an existing policy, the newly set
         // fields overwrite those from the previous policy, but unset fields in the new policy
@@ -2826,18 +2828,18 @@
 
         AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
         assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);  // from update
+                .isEqualTo(STATE_ALLOW);  // from update
         assertThat(savedRule.getZenPolicy().getPriorityCallSenders())
                 .isEqualTo(ZenPolicy.PEOPLE_TYPE_CONTACTS);  // from update
         assertThat(savedRule.getZenPolicy().getPriorityCategoryAlarms())
-                .isEqualTo(ZenPolicy.STATE_DISALLOW);  // from original
+                .isEqualTo(STATE_DISALLOW);  // from original
         assertThat(savedRule.getZenPolicy().getPriorityCategoryReminders())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);  // from original
+                .isEqualTo(STATE_ALLOW);  // from original
     }
 
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void addAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() {
         ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                 ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -2857,7 +2859,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void addAutomaticZenRule_withTypeBedtime_keepsEnabledSleeping() {
         ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                 ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -2878,7 +2880,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void addAutomaticZenRule_withTypeBedtime_keepsCustomizedSleeping() {
         ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                 ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID);
@@ -2899,8 +2901,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testSetManualZenMode() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         setupZenConfig();
 
         // note that caller=null because that's how it comes in from NMS.setZenMode
@@ -2919,68 +2921,83 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
-    @DisableFlags(Flags.FLAG_MODES_UI)
-    public void setManualZenMode_off_snoozesActiveRules(@TestParameter ChangeOrigin setZenOrigin) {
-        // Start with an active rule and an inactive rule.
-        mZenModeHelper.mConfig.automaticRules.clear();
-        AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .build();
-        String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
-                CUSTOM_PKG_UID);
-        AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .build();
-        String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+    @EnableFlags(FLAG_MODES_API)
+    @DisableFlags(FLAG_MODES_UI)
+    public void setManualZenMode_off_snoozesActiveRules() {
+        for (ChangeOrigin origin : ChangeOrigin.values()) {
+            // Start with an active rule and an inactive rule.
+            mZenModeHelper.mConfig.automaticRules.clear();
+            AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                    .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                    .build();
+            String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                    activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+            mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+                    CUSTOM_PKG_UID);
+            AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                    .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                    .build();
+            String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                    inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
 
-        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+            assertWithMessage("Failure for origin " + origin.name())
+                    .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
-        // User turns DND off.
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, setZenOrigin.value(),
-                "snoozing", "systemui", Process.SYSTEM_UID);
-        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
-        assertThat(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing).isTrue();
-        assertThat(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+            // User turns DND off.
+            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(),
+                    "snoozing", "systemui", Process.SYSTEM_UID);
+            assertWithMessage("Failure for origin " + origin.name())
+                    .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+            assertWithMessage("Failure for origin " + origin.name())
+                    .that(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing)
+                    .isTrue();
+            assertWithMessage("Failure for origin " + origin.name())
+                    .that(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing)
+                    .isFalse();
+        }
     }
 
     @Test
-    @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
-    public void setManualZenMode_off_doesNotSnoozeRulesIfFromUser(
-            @TestParameter ChangeOrigin setZenOrigin) {
-        // Start with an active rule and an inactive rule
-        mZenModeHelper.mConfig.automaticRules.clear();
-        AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .build();
-        String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
-                CUSTOM_PKG_UID);
-        AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .build();
-        String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
-                inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setManualZenMode_off_doesNotSnoozeRulesIfFromUser() {
+        for (ChangeOrigin origin : ChangeOrigin.values()) {
+            // Start with an active rule and an inactive rule
+            mZenModeHelper.mConfig.automaticRules.clear();
+            AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                    .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                    .build();
+            String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                    activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+            mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+                    CUSTOM_PKG_UID);
+            AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                    .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                    .build();
+            String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                    inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID);
 
-        assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-
-        // User turns DND off.
-        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, setZenOrigin.value(),
-                "snoozing", "systemui", Process.SYSTEM_UID);
-        ZenModeConfig config = mZenModeHelper.mConfig;
-        if (setZenOrigin == ChangeOrigin.ORIGIN_USER) {
-            // Other rule was unaffected.
             assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-            assertThat(config.automaticRules.get(activeRuleId).snoozing).isFalse();
-            assertThat(config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
-        } else {
-            assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
-            assertThat(config.automaticRules.get(activeRuleId).snoozing).isTrue();
-            assertThat(config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+
+            // User turns DND off.
+            mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(),
+                    "snoozing", "systemui", Process.SYSTEM_UID);
+            ZenModeConfig config = mZenModeHelper.mConfig;
+            if (origin == ChangeOrigin.ORIGIN_USER) {
+                // Other rule was unaffected.
+                assertWithMessage("Failure for origin " + origin.name()).that(
+                        mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+                assertWithMessage("Failure for origin " + origin.name()).that(
+                        config.automaticRules.get(activeRuleId).snoozing).isFalse();
+                assertWithMessage("Failure for origin " + origin.name()).that(
+                        config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+            } else {
+                assertWithMessage("Failure for origin " + origin.name()).that(
+                        mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
+                assertWithMessage("Failure for origin " + origin.name()).that(
+                        config.automaticRules.get(activeRuleId).snoozing).isTrue();
+                assertWithMessage("Failure for origin " + origin.name()).that(
+                        config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+            }
         }
     }
 
@@ -3002,45 +3019,17 @@
         assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode);
     }
 
-    private enum ModesFlag {
-        MODES_UI(2, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER),
-        MODES_API(1, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER),
-        DISABLED(0, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI);
-
-        private final int mFlagsEnabled;
-        @ConfigChangeOrigin
-        private final int mOriginForUserActionInSystemUi;
-
-        ModesFlag(int flagsEnabled, @ConfigChangeOrigin int originForUserActionInSystemUi) {
-            this.mFlagsEnabled = flagsEnabled;
-            this.mOriginForUserActionInSystemUi = originForUserActionInSystemUi;
-        }
-
-        void applyFlags(SetFlagsRule setFlagsRule) {
-            if (mFlagsEnabled >= 1) {
-                setFlagsRule.enableFlags(Flags.FLAG_MODES_API);
-            } else {
-                setFlagsRule.disableFlags(Flags.FLAG_MODES_API);
-            }
-            if (mFlagsEnabled >= 2) {
-                setFlagsRule.enableFlags(Flags.FLAG_MODES_UI);
-            } else {
-                setFlagsRule.disableFlags(Flags.FLAG_MODES_UI);
-            }
-        }
-    }
-
     @Test
-    public void testZenModeEventLog_setManualZenMode(@TestParameter ModesFlag modesFlag)
+    public void testZenModeEventLog_setManualZenMode()
             throws IllegalArgumentException {
-        modesFlag.applyFlags(mSetFlagsRule);
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
 
         // Turn zen mode on (to important_interruptions)
         // Need to additionally call the looper in order to finish the post-apply-config process
         mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                modesFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID);
+                Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null,
+                Process.SYSTEM_UID);
 
         // Now turn zen mode off, but via a different package UID -- this should get registered as
         // "not an action by the user" because some other app is changing zen mode
@@ -3068,7 +3057,7 @@
         assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0));
         assertEquals(1, mZenModeEventLogger.getNumRulesActive(0));
         assertThat(mZenModeEventLogger.getFromSystemOrSystemUi(0)).isEqualTo(
-                modesFlag == ModesFlag.DISABLED);
+                !(Flags.modesUi() || Flags.modesApi()));
         assertTrue(mZenModeEventLogger.getIsUserAction(0));
         assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(0));
         checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0));
@@ -3097,9 +3086,8 @@
     }
 
     @Test
-    public void testZenModeEventLog_automaticRules(@TestParameter ModesFlag modesFlag)
+    public void testZenModeEventLog_automaticRules()
             throws IllegalArgumentException {
-        modesFlag.applyFlags(mSetFlagsRule);
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
 
@@ -3116,15 +3104,14 @@
         // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE
         mZenModeHelper.setAutomaticZenRuleState(id,
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI,
-                Process.SYSTEM_UID);
+                UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
 
         // Event 2: "User" turns off the automatic rule (sets it to not enabled)
         zenRule.setEnabled(false);
         mZenModeHelper.updateAutomaticZenRule(id, zenRule,
-                modesFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID);
+                Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+                Process.SYSTEM_UID);
 
-        // Add a new system rule
         AutomaticZenRule systemRule = new AutomaticZenRule("systemRule",
                 null,
                 new ComponentName("android", "ScheduleConditionProvider"),
@@ -3132,7 +3119,8 @@
                 null,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule,
-                UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
+                Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test",
+                Process.SYSTEM_UID);
 
         // Event 3: turn on the system rule
         mZenModeHelper.setAutomaticZenRuleState(systemId,
@@ -3140,9 +3128,9 @@
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
 
         // Event 4: "User" deletes the rule
-        mZenModeHelper.removeAutomaticZenRule(systemId, modesFlag.mOriginForUserActionInSystemUi,
-                "", Process.SYSTEM_UID);
-
+        mZenModeHelper.removeAutomaticZenRule(systemId,
+                Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "",
+                Process.SYSTEM_UID);
         // In total, this represents 4 events
         assertEquals(4, mZenModeEventLogger.numLoggedChanges());
 
@@ -3201,7 +3189,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testZenModeEventLog_automaticRuleActivatedFromAppByAppAndUser()
             throws IllegalArgumentException {
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
@@ -3288,22 +3276,19 @@
     }
 
     @Test
-    public void testZenModeEventLog_policyChanges(@TestParameter ModesFlag modesFlag)
+    public void testZenModeEventLog_policyChanges()
             throws IllegalArgumentException {
-        modesFlag.applyFlags(mSetFlagsRule);
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
 
         // First just turn zen mode on
         mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
-                modesFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID);
+                UPDATE_ORIGIN_USER, "", null, Process.SYSTEM_UID);
 
         // Now change the policy slightly; want to confirm that this'll be reflected in the logs
         ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
-        newConfig.allowAlarms = true;
-        newConfig.allowRepeatCallers = false;
-        mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
-                modesFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID);
+        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0),
+                UPDATE_ORIGIN_USER, Process.SYSTEM_UID);
 
         // Turn zen mode off; we want to make sure policy changes do not get logged when zen mode
         // is off.
@@ -3311,10 +3296,8 @@
                 null, Process.SYSTEM_UID);
 
         // Change the policy again
-        newConfig.allowMessages = false;
-        newConfig.allowRepeatCallers = true;
-        mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
-                modesFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID);
+        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0),
+                UPDATE_ORIGIN_USER, Process.SYSTEM_UID);
 
         // Total events: we only expect ones for turning on, changing policy, and turning off
         assertEquals(3, mZenModeEventLogger.numLoggedChanges());
@@ -3347,9 +3330,7 @@
     }
 
     @Test
-    public void testZenModeEventLog_ruleCounts(@TestParameter ModesFlag modesFlag)
-            throws IllegalArgumentException {
-        modesFlag.applyFlags(mSetFlagsRule);
+    public void testZenModeEventLog_ruleCounts() throws IllegalArgumentException {
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
 
@@ -3374,14 +3355,12 @@
 
         // Rule 3, has stricter settings than the default settings
         ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy();
-        ruleConfig.allowReminders = false;
-        ruleConfig.allowCalls = false;
-        ruleConfig.allowMessages = false;
+        ruleConfig.applyNotificationPolicy(new Policy(0, 0, 0));
         AutomaticZenRule zenRule3 = new AutomaticZenRule("name3",
                 null,
                 new ComponentName("android", "ScheduleConditionProvider"),
                 ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
-                ruleConfig.toZenPolicy(),
+                ruleConfig.getZenPolicy(),
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3,
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID);
@@ -3452,10 +3431,8 @@
     }
 
     @Test
-    public void testZenModeEventLog_noLogWithNoConfigChange(
-            @TestParameter ModesFlag modesFlag) throws IllegalArgumentException {
+    public void testZenModeEventLog_noLogWithNoConfigChange() throws IllegalArgumentException {
         // If evaluateZenMode is called independently of a config change, don't log.
-        modesFlag.applyFlags(mSetFlagsRule);
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
 
@@ -3472,17 +3449,16 @@
     }
 
     @Test
-    public void testZenModeEventLog_reassignUid(@TestParameter ModesFlag modesFlag)
+    public void testZenModeEventLog_reassignUid()
             throws IllegalArgumentException {
         // Test that, only in specific cases, we reassign the calling UID to one associated with
         // the automatic rule owner.
-        modesFlag.applyFlags(mSetFlagsRule);
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
 
         // Explicitly set up all rules with the same policy as the manual rule so there will be
         // no policy changes in this test case.
-        ZenPolicy manualRulePolicy = mZenModeHelper.mConfig.toZenPolicy();
+        ZenPolicy manualRulePolicy = mZenModeHelper.mConfig.getZenPolicy();
 
         // Rule 1, owned by a package
         AutomaticZenRule zenRule = new AutomaticZenRule("name",
@@ -3502,7 +3478,7 @@
                 manualRulePolicy,
                 NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
         String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
-                modesFlag.mOriginForUserActionInSystemUi, "test", Process.SYSTEM_UID);
+                UPDATE_ORIGIN_USER, "test", Process.SYSTEM_UID);
 
         // Turn on rule 1; call looks like it's from the system. Because setting a condition is
         // typically an automatic (non-user-initiated) action, expect the calling UID to be
@@ -3521,7 +3497,7 @@
         // from the system-provided one.
         zenRule.setEnabled(false);
         mZenModeHelper.updateAutomaticZenRule(id, zenRule,
-                modesFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID);
+                UPDATE_ORIGIN_USER, "", Process.SYSTEM_UID);
 
         // Add a manual rule. Any manual rule changes should not get calling uids reassigned.
         mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
@@ -3578,10 +3554,8 @@
     }
 
     @Test
-    public void testZenModeEventLog_channelsBypassingChanges(
-            @TestParameter ModesFlag modesFlag) {
+    public void testZenModeEventLog_channelsBypassingChanges() {
         // Verify that the right thing happens when the canBypassDnd value changes.
-        modesFlag.applyFlags(mSetFlagsRule);
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
 
@@ -3589,21 +3563,25 @@
         // as a user action, and *should* get its UID reassigned.
         mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", CUSTOM_PKG_NAME, Process.SYSTEM_UID);
+        assertEquals(1, mZenModeEventLogger.numLoggedChanges());
 
         // Now change apps bypassing to true
         ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
         newConfig.areChannelsBypassingDnd = true;
         mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+        assertEquals(2, mZenModeEventLogger.numLoggedChanges());
 
         // and then back to false, all without changing anything else
         newConfig.areChannelsBypassingDnd = false;
         mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
+        assertEquals(3, mZenModeEventLogger.numLoggedChanges());
 
         // Turn off manual mode, call from a package: don't reset UID even though enabler is set
         mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "",
                 CUSTOM_PKG_NAME, 12345);
+        assertEquals(4, mZenModeEventLogger.numLoggedChanges());
 
         // And likewise when turning it back on again
         mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP,
@@ -3642,11 +3620,11 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testZenModeEventLog_policyAllowChannels() {
         // when modes_api flag is on, ensure that any change in allow_channels gets logged,
         // even when there are no other changes.
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
 
         // Default zen config has allow channels = priority (aka on)
         setupZenConfig();
@@ -3658,8 +3636,12 @@
         // Now change only the channels part of the policy; want to confirm that this'll be
         // reflected in the logs
         ZenModeConfig newConfig = mZenModeHelper.mConfig.copy();
-        newConfig.allowPriorityChannels = false;
-        mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(),
+        Policy oldPolicy = newConfig.toNotificationPolicy();
+        Policy newPolicy = new Policy(oldPolicy.priorityCategories, oldPolicy.priorityCallSenders,
+                oldPolicy.priorityMessageSenders, oldPolicy.suppressedVisualEffects,
+                STATE_PRIORITY_CHANNELS_BLOCKED,
+                oldPolicy.priorityConversationSenders);
+        mZenModeHelper.setNotificationPolicy(newPolicy,
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
 
         // Total events: one for turning on, one for changing policy
@@ -3688,7 +3670,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() {
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
@@ -3730,7 +3712,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testZenModeEventLog_activeRuleTypes() {
         mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true);
         setupZenConfig();
@@ -3748,7 +3730,7 @@
         // Create immersive rule
         AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID)
                 .setType(TYPE_IMMERSIVE)
-                .setZenPolicy(mZenModeHelper.mConfig.toZenPolicy()) // same as the manual rule
+                .setZenPolicy(mZenModeHelper.mConfig.getZenPolicy()) // same as the manual rule
                 .build();
         String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive, UPDATE_ORIGIN_APP,
                 "reason", CUSTOM_PKG_UID);
@@ -3819,10 +3801,9 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_MODES_API)
+    @DisableFlags(FLAG_MODES_API)
     public void testUpdateConsolidatedPolicy_preModesApiDefaultRulesOnly_takesGlobalDefault() {
         setupZenConfig();
-
         // When there's one automatic rule active and it doesn't specify a policy, test that the
         // resulting consolidated policy is one that matches the default rule settings.
         AutomaticZenRule zenRule = new AutomaticZenRule("name",
@@ -3839,6 +3820,9 @@
                 new Condition(zenRule.getConditionId(), "", STATE_TRUE),
                 UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID);
 
+        assertEquals(mZenModeHelper.getNotificationPolicy(),
+                mZenModeHelper.getConsolidatedNotificationPolicy());
+
         // inspect the consolidated policy. Based on setupZenConfig() values.
         assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms());
         assertFalse(mZenModeHelper.mConsolidatedPolicy.allowMedia());
@@ -3853,9 +3837,7 @@
     }
 
     @Test
-    public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault(
-            @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) {
-        modesFlag.applyFlags(mSetFlagsRule);
+    public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault() {
         setupZenConfig();
 
         // When there's one automatic rule active and it doesn't specify a policy, test that the
@@ -3876,13 +3858,13 @@
 
         // inspect the consolidated policy, which should match the device default settings.
         assertThat(ZenAdapters.notificationPolicyToZenPolicy(mZenModeHelper.mConsolidatedPolicy))
-                .isEqualTo(modesFlag == ModesFlag.MODES_UI
+                .isEqualTo(Flags.modesUi()
                         ? mZenModeHelper.getDefaultZenPolicy()
-                        : mZenModeHelper.mConfig.toZenPolicy());
+                        : mZenModeHelper.mConfig.getZenPolicy());
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_MODES_API)
+    @DisableFlags(FLAG_MODES_API)
     public void testUpdateConsolidatedPolicy_preModesApiCustomPolicyOnly_fillInWithGlobal() {
         setupZenConfig();
 
@@ -3928,9 +3910,8 @@
     }
 
     @Test
-    public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault(
-            @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) {
-        modesFlag.applyFlags(mSetFlagsRule);
+    @EnableFlags(FLAG_MODES_API)
+    public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault() {
         setupZenConfig();
 
         // when there's only one automatic rule active and it has a custom policy, make sure that's
@@ -3962,12 +3943,12 @@
         // policy for every field specified, and take default values (from either device default
         // policy or manual rule) for unspecified things
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isEqualTo(
-                modesFlag == ModesFlag.MODES_UI ? true : false);  // default
+                Flags.modesUi() ? true : false);  // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isEqualTo(
-                modesFlag == ModesFlag.MODES_UI ? true : false);  // default
+                Flags.modesUi() ? true : false);  // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isTrue();  // custom
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo(
-                modesFlag == ModesFlag.MODES_UI ? false : true);  // default
+                Flags.modesUi() ? false : true);  // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isFalse();  // custom
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()).isFalse();  // custom
@@ -3976,7 +3957,7 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_MODES_API)
+    @DisableFlags(FLAG_MODES_API)
     public void testUpdateConsolidatedPolicy_preModesApiDefaultAndCustomActive_mergesWithGlobal() {
         setupZenConfig();
 
@@ -4037,9 +4018,8 @@
     }
 
     @Test
-    public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault(
-            @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) {
-        modesFlag.applyFlags(mSetFlagsRule);
+    @EnableFlags(FLAG_MODES_API)
+    public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault() {
         setupZenConfig();
 
         // when there are two rules active, one inheriting the default policy and one setting its
@@ -4088,10 +4068,10 @@
         // restrictive option of each of the two
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isFalse();  // custom stricter
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isEqualTo(
-                modesFlag == ModesFlag.MODES_UI ? true : false);  // default
+                Flags.modesUi() ? true : false);  // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isFalse();  // default stricter
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo(
-                modesFlag == ModesFlag.MODES_UI ? false : true);  // default
+                Flags.modesUi() ? false : true);  // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isFalse();  // custom stricter
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue();  // default
@@ -4099,12 +4079,12 @@
                 .isFalse();  // custom stricter
         assertThat(mZenModeHelper.mConsolidatedPolicy.showBadges()).isFalse();  // custom stricter
         assertThat(mZenModeHelper.mConsolidatedPolicy.showPeeking()).isEqualTo(
-                modesFlag == ModesFlag.MODES_UI ? false : true);  // default
+                Flags.modesUi() ? false : true);  // default
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testUpdateConsolidatedPolicy_allowChannels() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         setupZenConfig();
 
         // one rule, custom policy, allows channels
@@ -4153,9 +4133,8 @@
     }
 
     @Test
-    public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll(
-            @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) {
-        modesFlag.applyFlags(mSetFlagsRule);
+    @EnableFlags(FLAG_MODES_API)
+    public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() {
         setupZenConfig();
 
         // Rules with INTERRUPTION_FILTER_ALL are skipped when calculating consolidated policy.
@@ -4193,11 +4172,11 @@
 
         // Consolidated Policy should be default + rule1.
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isEqualTo(
-                modesFlag == ModesFlag.MODES_UI ? true : false);  // default
+                Flags.modesUi() ? true : false);  // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isTrue(); // priority rule
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isTrue();  // priority rule
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo(
-                modesFlag == ModesFlag.MODES_UI ? false : true);  // default
+                Flags.modesUi() ? false : true);  // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isTrue();  // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default
         assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue();  // default
@@ -4205,8 +4184,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void zenRuleToAutomaticZenRule_allFields() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
                 new String[]{OWNER.getPackageName()});
 
@@ -4249,8 +4228,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void automaticZenRuleToZenRule_allFields() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
                 new String[]{OWNER.getPackageName()});
 
@@ -4291,7 +4270,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_fromApp_updatesNameUnlessUserModified() {
         // Add a starting rule with the name OriginalName.
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID)
@@ -4348,7 +4327,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_fromUser_updatesBitmaskAndValue() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4385,7 +4364,7 @@
         assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
         assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID);
         assertThat(rule.getZenPolicy().getPriorityChannelsAllowed()).isEqualTo(
-                ZenPolicy.STATE_DISALLOW);
+                STATE_DISALLOW);
 
         assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
 
@@ -4401,7 +4380,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_fromSystemUi_updatesValues() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4441,7 +4420,7 @@
         // UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask.
         assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID);
         assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);
+                .isEqualTo(STATE_ALLOW);
         assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
 
         ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -4451,7 +4430,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_fromApp_updatesValuesIfRuleNotUserModified() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4490,7 +4469,7 @@
 
         assertThat(storedRule.zenMode).isEqualTo(ZEN_MODE_ALARMS);
         assertThat(storedRule.zenPolicy.getPriorityCategoryReminders())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);
+                .isEqualTo(STATE_ALLOW);
         assertThat(storedRule.zenDeviceEffects.shouldDisplayGrayscale()).isTrue();
         assertThat(storedRule.userModifiedFields).isEqualTo(0);
         assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(0);
@@ -4514,7 +4493,7 @@
         // so the rule is not changed, and neither is the bitmask.
         assertThat(ruleUser.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
         assertThat(ruleUser.getZenPolicy().getPriorityCategoryReminders())
-                .isEqualTo(ZenPolicy.STATE_DISALLOW);
+                .isEqualTo(STATE_DISALLOW);
         assertThat(ruleUser.getDeviceEffects().shouldDisplayGrayscale()).isFalse();
 
         storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleIdUser);
@@ -4525,7 +4504,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void addAutomaticZenRule_updatesValues() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4543,7 +4522,7 @@
 
         // The values are modified but the bitmask is not.
         assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
-                .isEqualTo(ZenPolicy.STATE_ALLOW);
+                .isEqualTo(STATE_ALLOW);
         assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue();
 
         ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
@@ -4551,7 +4530,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_nullDeviceEffectsUpdate() {
         // Adds a starting rule with empty zen policies and device effects
         ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
@@ -4578,7 +4557,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_nullPolicyUpdate() {
         // Adds a starting rule with set zen policy and empty device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4607,7 +4586,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void automaticZenRuleToZenRule_nullToNonNullPolicyUpdate() {
         when(mContext.checkCallingPermission(anyString()))
                 .thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -4654,7 +4633,7 @@
         // New ZenPolicy differs from the default config
         assertThat(rule.getZenPolicy()).isNotNull();
         assertThat(rule.getZenPolicy().getPriorityChannelsAllowed()).isEqualTo(
-                ZenPolicy.STATE_DISALLOW);
+                STATE_DISALLOW);
 
         ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(storedRule.canBeUpdatedByApp()).isFalse();
@@ -4671,7 +4650,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void automaticZenRuleToZenRule_nullToNonNullDeviceEffectsUpdate() {
         // Adds a starting rule with empty zen policies and device effects
         AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID)
@@ -4775,8 +4754,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testUpdateAutomaticRule_activated_triggersBroadcast() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         setupZenConfig();
 
         // Add a new automatic zen rule that's enabled
@@ -4815,8 +4794,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testUpdateAutomaticRule_deactivatedByUser_triggersBroadcast() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         setupZenConfig();
 
         // Add a new automatic zen rule that's enabled
@@ -4860,8 +4839,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testUpdateAutomaticRule_deactivatedByApp_triggersBroadcast() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API);
         setupZenConfig();
 
         // Add a new automatic zen rule that's enabled
@@ -4937,7 +4916,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_ruleChanged_deactivatesRule() {
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
         AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -4961,7 +4940,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void updateAutomaticZenRule_ruleNotChanged_doesNotDeactivateRule() {
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
         AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -4984,7 +4963,7 @@
     }
 
     @Test
-    @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
     public void updateAutomaticZenRule_ruleChangedByUser_doesNotDeactivateRule() {
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
         AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -5009,7 +4988,7 @@
     }
 
     @Test
-    @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
     public void updateAutomaticZenRule_ruleDisabledByUser_doesNotReactivateOnReenable() {
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
         AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID)
@@ -5034,7 +5013,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() {
         mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
         reset(mDeviceEffectsApplier);
@@ -5057,8 +5036,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testDeviceEffects_applied() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
         verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
 
@@ -5077,8 +5056,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testDeviceEffects_onDeactivateRule_applied() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
 
         ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
@@ -5096,8 +5075,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testDeviceEffects_changeToConsolidatedEffects_applied() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
         verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
 
@@ -5136,8 +5115,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testDeviceEffects_noChangeToConsolidatedEffects_notApplied() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
         verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
 
@@ -5161,9 +5140,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testDeviceEffects_activeBeforeApplierProvided_appliedWhenProvided() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
         ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build();
         String ruleId = addRuleWithEffects(zde);
         verify(mDeviceEffectsApplier, never()).apply(any(), anyInt());
@@ -5178,8 +5156,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testDeviceEffects_onUserSwitch_appliedImmediately() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier);
         verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT));
 
@@ -5214,9 +5192,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void removeAndAddAutomaticZenRule_wasCustomized_isRestored() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
         // Start with a rule.
         mZenModeHelper.mConfig.automaticRules.clear();
         mTestClock.setNowMillis(1000);
@@ -5257,7 +5234,7 @@
         assertThat(newRuleId).isEqualTo(ruleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
         assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo(
-                ZenPolicy.STATE_ALLOW);
+                STATE_ALLOW);
 
         ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(storedRule.userModifiedFields).isEqualTo(
@@ -5270,9 +5247,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void removeAndAddAutomaticZenRule_wasNotCustomized_isNotRestored() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
         // Start with a single rule.
         mZenModeHelper.mConfig.automaticRules.clear();
         mTestClock.setNowMillis(1000);
@@ -5303,9 +5279,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void removeAndAddAutomaticZenRule_recreatedButNotByApp_isNotRestored() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
         // Start with a single rule.
         mZenModeHelper.mConfig.automaticRules.clear();
         mTestClock.setNowMillis(1000);
@@ -5345,16 +5320,15 @@
         assertThat(newRuleId).isNotEqualTo(ruleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
         assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo(
-                ZenPolicy.STATE_DISALLOW);
+                STATE_DISALLOW);
 
         // Also, we discarded the "deleted rule" since we're not interested in recreating it.
         assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void removeAndAddAutomaticZenRule_removedByUser_isNotRestored() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
         // Start with a single rule.
         mZenModeHelper.mConfig.automaticRules.clear();
         mTestClock.setNowMillis(1000);
@@ -5394,8 +5368,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
                 PERMISSION_GRANTED); // So that canManageAZR passes although packages don't match.
         mZenModeHelper.mConfig.automaticRules.clear();
@@ -5434,8 +5408,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void removeAllZenRules_preservedForRestoring() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
@@ -5456,8 +5430,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void removeAllZenRules_fromSystem_deletesPreservedRulesToo() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         // Start with deleted rules from 2 different packages.
@@ -5476,7 +5450,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void removeAndAddAutomaticZenRule_wasActive_isRestoredAsInactive() {
         // Start with a rule.
         mZenModeHelper.mConfig.automaticRules.clear();
@@ -5525,7 +5499,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void removeAndAddAutomaticZenRule_wasSnoozed_isRestoredAsInactive() {
         // Start with a rule.
         mZenModeHelper.mConfig.automaticRules.clear();
@@ -5579,8 +5553,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void testRuleCleanup() throws Exception {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         Instant now = Instant.ofEpochMilli(1701796461000L);
         Instant yesterday = now.minus(1, ChronoUnit.DAYS);
         Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS);
@@ -5637,7 +5611,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void getAutomaticZenRuleState_ownedRule_returnsRuleState() {
         String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
@@ -5662,7 +5636,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() {
         // Assume existence of a system-owned rule that is currently ACTIVE.
         ZenRule systemRule = newZenRule("android", Instant.now(), null);
@@ -5678,7 +5652,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() {
         // Assume existence of an other-package-owned rule that is currently ACTIVE.
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
@@ -5699,7 +5673,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() {
         // Assume existence of an other-package-owned rule that is currently ACTIVE.
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
@@ -5720,7 +5694,7 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testCallbacks_policy() throws Exception {
         setupZenConfig();
         assertThat(mZenModeHelper.getNotificationPolicy().allowReminders()).isTrue();
@@ -5740,10 +5714,9 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void testCallbacks_consolidatedPolicy() throws Exception {
-        setupZenConfig();
-        assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowAlarms()).isTrue();
+        assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowMedia()).isTrue();
         SettableFuture<Policy> futureConsolidatedPolicy = SettableFuture.create();
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
@@ -5762,12 +5735,12 @@
                 new Condition(CONDITION_ID, "", STATE_TRUE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID);
 
         Policy callbackPolicy = futureConsolidatedPolicy.get(1, TimeUnit.SECONDS);
-        assertThat(callbackPolicy.allowAlarms()).isFalse();
+        assertThat(callbackPolicy.allowMedia()).isFalse();
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -5777,13 +5750,13 @@
                 .comparingElementsUsing(IGNORE_METADATA)
                 .containsExactly(
                         expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
-                                mZenModeHelper.mConfig.toZenPolicy(), // copy of global config
+                                mZenModeHelper.mConfig.getZenPolicy(), // copy of global config
                                 true));
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -5798,12 +5771,12 @@
                 .comparingElementsUsing(IGNORE_METADATA)
                 .containsExactly(
                         expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_ALARMS,
-                                mZenModeHelper.mConfig.toZenPolicy(), // copy of global config
+                                mZenModeHelper.mConfig.getZenPolicy(), // copy of global config
                                 true));
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() {
         mZenModeHelper.mConfig.automaticRules.clear();
         String pkg = mContext.getPackageName();
@@ -5835,7 +5808,7 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_ruleCustomizedButNotFilter_updatesRule() {
         mZenModeHelper.mConfig.automaticRules.clear();
         String pkg = mContext.getPackageName();
@@ -5866,8 +5839,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
         mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS);
@@ -5883,8 +5856,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -5894,8 +5867,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
@@ -5915,8 +5888,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MODES_API)
     public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() {
-        mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         withoutWtfCrash(
@@ -5928,8 +5901,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalPolicyAsImplicitZenRule_createsImplicitRule() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
@@ -5952,8 +5925,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalPolicyAsImplicitZenRule_updatesImplicitRule() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         Policy original = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
@@ -5983,7 +5956,7 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalPolicyAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() {
         mZenModeHelper.mConfig.automaticRules.clear();
         String pkg = mContext.getPackageName();
@@ -5995,7 +5968,7 @@
 
         // Store this for checking later.
         ZenPolicy originalEffectiveZenPolicy = new ZenPolicy.Builder(
-                mZenModeHelper.mConfig.toZenPolicy()).allowMedia(true).build();
+                mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
 
         // From user, update that rule's policy.
         AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
@@ -6023,7 +5996,7 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    @EnableFlags(FLAG_MODES_API)
     public void applyGlobalPolicyAsImplicitZenRule_ruleCustomizedButNotZenPolicy_updatesRule() {
         mZenModeHelper.mConfig.automaticRules.clear();
         String pkg = mContext.getPackageName();
@@ -6035,7 +6008,7 @@
 
         // Store this for checking later.
         ZenPolicy originalEffectiveZenPolicy = new ZenPolicy.Builder(
-                mZenModeHelper.mConfig.toZenPolicy()).allowMedia(true).build();
+                mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
 
         // From user, update something in that rule, but not the ZenPolicy.
         AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId);
@@ -6060,8 +6033,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_MODES_API)
     public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() {
-        mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API);
         mZenModeHelper.mConfig.automaticRules.clear();
 
         withoutWtfCrash(
@@ -6072,8 +6045,8 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void getNotificationPolicyFromImplicitZenRule_returnsSetPolicy() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
         Policy writtenPolicy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS,
                 PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED,
                 Policy.getAllSuppressedVisualEffects(), STATE_FALSE,
@@ -6088,32 +6061,30 @@
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
+    @DisableFlags(FLAG_MODES_UI)
     public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_copiesGlobalPolicy() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-        mZenModeHelper.mConfig.allowCalls = true;
-        mZenModeHelper.mConfig.allowConversations = false;
-
         // Implicit rule will get the global policy at the time of rule creation.
         mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
-                ZEN_MODE_ALARMS);
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
-        // If the policy then changes afterwards, we should keep the snapshotted version.
-        mZenModeHelper.mConfig.allowCalls = false;
-
+        // If the policy then changes afterwards, it should inherit updates because user cannot
+        // edit the policy in the UI.
+        mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0),
+                UPDATE_ORIGIN_APP, 1);
         Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
                 CUSTOM_PKG_NAME);
 
         assertThat(readPolicy).isNotNull();
-        assertThat(readPolicy.allowCalls()).isTrue();
-        assertThat(readPolicy.allowConversations()).isFalse();
+        assertThat(readPolicy.allowCalls()).isFalse();
+        assertThat(readPolicy.allowAlarms()).isTrue();
     }
 
     @Test
+    @EnableFlags(FLAG_MODES_API)
     public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() {
-        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
-
-        mZenModeHelper.mConfig.allowCalls = true;
-        mZenModeHelper.mConfig.allowConversations = false;
+        Policy policy = new Policy(PRIORITY_CATEGORY_CALLS, PRIORITY_SENDERS_STARRED, 0);
+        mZenModeHelper.setNotificationPolicy(policy, UPDATE_ORIGIN_USER, 1);
 
         Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule(
                 CUSTOM_PKG_NAME);
@@ -6124,8 +6095,8 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_API)
-    @DisableFlags(Flags.FLAG_MODES_UI)
+    @EnableFlags(FLAG_MODES_API)
+    @DisableFlags(FLAG_MODES_UI)
     public void setNotificationPolicy_updatesRulePolicies_ifRulePolicyIsDefaultOrGlobalPolicy() {
         ZenPolicy defaultZenPolicy = mZenModeHelper.getDefaultZenPolicy();
         Policy previousManualPolicy = mZenModeHelper.mConfig.toNotificationPolicy();
@@ -6185,6 +6156,35 @@
         assertThat(storedRule.getIconResId()).isEqualTo(0);
     }
 
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setManualZenRuleDeviceEffects_noPreexistingMode() {
+        ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+                .setShouldDimWallpaper(true)
+                        .build();
+        mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000);
+
+        assertThat(mZenModeHelper.getConfig().manualRule).isNotNull();
+        assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse();
+        assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects);
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setManualZenRuleDeviceEffects_preexistingMode() {
+        mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, UPDATE_ORIGIN_USER,
+                "create manual rule", "settings", 1000);
+
+        ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+                .setShouldDimWallpaper(true)
+                .build();
+        mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000);
+
+        assertThat(mZenModeHelper.getConfig().manualRule).isNotNull();
+        assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse();
+        assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects);
+    }
+
     private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
             @Nullable ZenPolicy zenPolicy) {
         ZenRule rule = new ZenRule();
@@ -6246,34 +6246,19 @@
 
     // TODO: b/310620812 - Update setup methods to include allowChannels() when MODES_API is inlined
     private void setupZenConfig() {
-        mZenModeHelper.mZenMode = ZEN_MODE_OFF;
-        mZenModeHelper.mConfig.allowAlarms = false;
-        mZenModeHelper.mConfig.allowMedia = false;
-        mZenModeHelper.mConfig.allowSystem = false;
-        mZenModeHelper.mConfig.allowReminders = true;
-        mZenModeHelper.mConfig.allowCalls = true;
-        mZenModeHelper.mConfig.allowCallsFrom = PRIORITY_SENDERS_STARRED;
-        mZenModeHelper.mConfig.allowMessages = true;
-        mZenModeHelper.mConfig.allowConversations = true;
-        mZenModeHelper.mConfig.allowEvents = true;
-        mZenModeHelper.mConfig.allowRepeatCallers = true;
-        mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE;
-        mZenModeHelper.mConfig.manualRule = null;
-    }
-
-    private void setupZenConfigMaintained() {
-        // config is still the same as when it was setup (setupZenConfig)
-        assertFalse(mZenModeHelper.mConfig.allowAlarms);
-        assertFalse(mZenModeHelper.mConfig.allowMedia);
-        assertFalse(mZenModeHelper.mConfig.allowSystem);
-        assertTrue(mZenModeHelper.mConfig.allowReminders);
-        assertTrue(mZenModeHelper.mConfig.allowCalls);
-        assertEquals(PRIORITY_SENDERS_STARRED, mZenModeHelper.mConfig.allowCallsFrom);
-        assertTrue(mZenModeHelper.mConfig.allowMessages);
-        assertTrue(mZenModeHelper.mConfig.allowConversations);
-        assertTrue(mZenModeHelper.mConfig.allowEvents);
-        assertTrue(mZenModeHelper.mConfig.allowRepeatCallers);
-        assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelper.mConfig.suppressedVisualEffects);
+        Policy customPolicy = new Policy(PRIORITY_CATEGORY_REMINDERS
+                | PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_MESSAGES
+                | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REPEAT_CALLERS
+                | PRIORITY_CATEGORY_CONVERSATIONS,
+                PRIORITY_SENDERS_STARRED,
+                PRIORITY_SENDERS_STARRED,
+                SUPPRESSED_EFFECT_BADGE,
+                0,
+                CONVERSATION_SENDERS_IMPORTANT);
+        mZenModeHelper.setNotificationPolicy(customPolicy, UPDATE_ORIGIN_UNKNOWN, 1);
+        if (!Flags.modesUi()) {
+            mZenModeHelper.mConfig.manualRule = null;
+        }
     }
 
     private void checkDndProtoMatchesSetupZenConfig(DNDPolicyProto dndProto) {
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 19ce217..9dac23f 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -1681,7 +1681,7 @@
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
                 .compose();
         VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100);
-        VibrationEffect effect5 = VibrationEffect.createOneShot(20, 222);
+        VibrationEffect effect5 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
 
         long vibrationId1 = startThreadAndDispatcher(effect1);
         waitForCompletion();
@@ -1745,13 +1745,12 @@
         verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
         assertTrue("Tested duration=" + duration4, duration4 < 2000);
 
-        // Effect5: normal oneshot. Don't worry about amplitude, as effect4 may or may not have
-        // started.
+        // Effect5: played normally after effect4, which may or may not have played.
 
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId5));
         verifyCallbacksTriggered(vibrationId5, Vibration.Status.FINISHED);
 
-        assertEquals(Arrays.asList(expectedOneShot(20)),
+        assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
                 fakeVibrator.getEffectSegments(vibrationId5));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index a80e2f8..2480913 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -54,6 +54,8 @@
 
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.policy.keyguard.KeyguardServiceDelegate;
+import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.DisplayPolicy;
 import com.android.server.wm.DisplayRotation;
@@ -68,7 +70,7 @@
  * Test class for {@link PhoneWindowManager}.
  *
  * Build/Install/Run:
- *  atest WmTests:PhoneWindowManagerTests
+ * atest WmTests:PhoneWindowManagerTests
  */
 @SmallTest
 public class PhoneWindowManagerTests {
@@ -78,6 +80,7 @@
 
     PhoneWindowManager mPhoneWindowManager;
     private ActivityTaskManagerInternal mAtmInternal;
+    private StatusBarManagerInternal mStatusBarManagerInternal;
     private Context mContext;
 
     @Before
@@ -90,6 +93,9 @@
         LocalServices.addService(ActivityTaskManagerInternal.class, mAtmInternal);
         mPhoneWindowManager.mActivityTaskManagerInternal = mAtmInternal;
         LocalServices.addService(WindowManagerInternal.class, mock(WindowManagerInternal.class));
+        mStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
+        LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
+        mPhoneWindowManager.mKeyguardDelegate = mock(KeyguardServiceDelegate.class);
     }
 
     @After
@@ -98,6 +104,7 @@
         reset(mContext);
         LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
     }
 
     @Test
@@ -206,6 +213,20 @@
         assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
     }
 
+    @Test
+    public void userSwitching_keyboardShortcutHelperDismissed() {
+        mPhoneWindowManager.setSwitchingUser(true);
+
+        verify(mStatusBarManagerInternal).dismissKeyboardShortcutsMenu();
+    }
+
+    @Test
+    public void userNotSwitching_keyboardShortcutHelperDismissed() {
+        mPhoneWindowManager.setSwitchingUser(false);
+
+        verify(mStatusBarManagerInternal, never()).dismissKeyboardShortcutsMenu();
+    }
+
     private void mockStartDockOrHome() throws Exception {
         doNothing().when(ActivityManager.getService()).stopAppSwitches();
         when(mAtmInternal.startHomeOnDisplay(
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 2030b30..ff1c6c8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -16,10 +16,8 @@
 
 package com.android.server.wm;
 
-import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
 import static android.app.Activity.RESULT_CANCELED;
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
-import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.START_ABORTED;
 import static android.app.ActivityManager.START_CANCELED;
 import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
@@ -44,7 +42,6 @@
 import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
@@ -52,7 +49,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -79,15 +75,12 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.notNull;
 
 import android.app.ActivityOptions;
-import android.app.ActivityOptions.BackgroundActivityStartMode;
 import android.app.AppOpsManager;
-import android.app.BackgroundStartPrivileges;
 import android.app.IApplicationThread;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
@@ -101,10 +94,10 @@
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
-import android.os.Process;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.provider.DeviceConfig;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.Pair;
@@ -116,21 +109,17 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.DeviceConfigStateHelper;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.PackageArchiver;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
 import com.android.server.wm.utils.MockTracker;
+import com.android.wm.shell.Flags;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -506,7 +495,8 @@
 
         // Start activity and delivered new intent.
         starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
-        doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+        doReturn(splitSecondReusableActivity)
+                .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
         final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
 
         // Ensure result is delivering intent to top.
@@ -533,7 +523,8 @@
 
         // Start activity and delivered new intent.
         starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
-        doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+        doReturn(splitSecondReusableActivity)
+                .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
         final int result = starter.setReason("testSplitScreenMoveToFront").execute();
 
         // Ensure result is moving task to front.
@@ -580,7 +571,7 @@
 
         // Start activity and delivered new intent.
         starter.getIntent().setComponent(activities.get(3).mActivityComponent);
-        doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any());
+        doReturn(activities.get(3)).when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
         final int result = starter.setReason("testDesktopModeDeliverToTop").execute();
 
         // Ensure result is delivering intent to top.
@@ -607,7 +598,8 @@
 
         // Start activity and delivered new intent.
         starter.getIntent().setComponent(desktopModeReusableActivity.mActivityComponent);
-        doReturn(desktopModeReusableActivity).when(mRootWindowContainer).findTask(any(), any());
+        doReturn(desktopModeReusableActivity)
+                .when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
         final int result = starter.setReason("testDesktopModeMoveToFront").execute();
 
         // Ensure result is moving task to front.
@@ -726,497 +718,68 @@
                 eq(FAKE_REAL_CALLING_UID), anyInt(), anyBoolean(), eq(false));
     }
 
-    /**
-     * This test ensures that unsupported usecases aren't aborted when background starts are
-     * allowed.
-     */
-    @Test
-    public void testBackgroundActivityStartsAllowed_noStartsAborted() {
-        doReturn(true).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest("allowed_noStartsAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
 
     /**
      * This test ensures that unsupported usecases are aborted when background starts are
      * disallowed.
      */
     @Test
-    public void testBackgroundActivityStartsDisallowed_unsupportedUsecaseAborted() {
+    public void testPinnedSingleInstanceAborted() {
         doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_unsupportedUsecase_aborted", true,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that unsupported usecases are aborted when background starts are
-     * disallowed.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_callingUidProcessStateTopAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callingUidProcessStateTop_aborted", true,
-                UNIMPORTANT_UID, false, PROCESS_STATE_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that unsupported usecases are aborted when background starts are
-     * disallowed.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_realCallingUidProcessStateTopAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_realCallingUidProcessStateTop_aborted", true,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that unsupported usecases are aborted when background starts are
-     * disallowed.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_hasForegroundActivitiesAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_hasForegroundActivities_aborted", true,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                true, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that unsupported usecases are aborted when background starts are
-     * disallowed.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_pinnedSingleInstanceAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_pinned_singleinstance_aborted", true,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, true, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the calling process runs as ROOT_UID.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_rootUidNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest("disallowed_rootUid_notAborted", false,
-                Process.ROOT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the calling process is running as SYSTEM_UID.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_systemUidNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest("disallowed_systemUid_notAborted", false,
-                Process.SYSTEM_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the calling process is running as NFC_UID.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_nfcUidNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest("disallowed_nfcUid_notAborted", false,
-                Process.NFC_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the calling process has a visible window.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_callingUidHasVisibleWindowNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callingUidHasVisibleWindow_notAborted", false,
-                UNIMPORTANT_UID, true, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * The sending app has a visible window, but does not (by default) allow the pending intent to
-     * start the background activity.
-     */
-    @Test
-    @Ignore("b/266015587")
-    public void testBackgroundActivityStartsDisallowed_realCallingUidHasVisibleWindowAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_realCallingUidHasVisibleWindow_abortedInU", true,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, true, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the caller is in the recent activity list.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_callerIsRecentsNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callerIsRecents_notAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, true, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the caller is temporarily (10s) allowed to start.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_callerIsAllowedNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callerIsAllowed_notAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, true, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the caller explicitly has background activity start privilege.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_callerIsInstrumentingWithBASPnotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callerIsInstrumentingWithBackgroundActivityStartPrivileges_notAborted",
-                false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, true, false, false, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the caller is a device owner.
-     */
-    @Test
-    public void
-            testBackgroundActivityStartsDisallowed_callingPackageNameIsDeviceOwnerNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callingPackageNameIsDeviceOwner_notAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, true, false, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the caller is a affiliated profile owner.
-     */
-    @Test
-    public void
-            testBackgroundActivityStartsDisallowed_isAffiliatedProfileOwnerNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callingUidIsAffiliatedProfileOwner_notAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, true, false, false);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the caller has the OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_callerHasSystemExemptAppOpNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callerHasSystemExemptAppOpNotAborted", false,
-                UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, true);
-    }
-
-    /**
-     * This test ensures that supported usecases aren't aborted when background starts are
-     * disallowed. Each scenarios tests one condition that makes them supported in isolation. In
-     * this case the caller is an IME.
-     */
-    @Test
-    public void testBackgroundActivityStartsDisallowed_callingPackageNameIsImeNotAborted() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        setupImeWindow();
-        runAndVerifyBackgroundActivityStartsSubtest(
-                "disallowed_callingPackageNameIsIme_notAborted", false,
-                CURRENT_IME_UID, false, PROCESS_STATE_BOUND_TOP,
-                UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                false, false, false, false, false, false, false, false);
-    }
-
-    /**
-     * This test ensures proper logging for BAL_ALLOW_PERMISSION.
-     */
-    @Test
-    public void testBackgroundActivityStartsAllowed_logging() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        MockitoSession mockingSession = mockitoSession()
-                .mockStatic(ActivityTaskManagerService.class)
-                .mockStatic(FrameworkStatsLog.class)
-                .strictness(Strictness.LENIENT)
-                .startMocking();
-        try {
-            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
-                    eq(START_ACTIVITIES_FROM_BACKGROUND),
-                    anyInt(), anyInt()));
-            runAndVerifyBackgroundActivityStartsSubtest(
-                    "allowed_notAborted", false,
-                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                    UNIMPORTANT_UID2, false, PROCESS_STATE_BOUND_TOP,
-                    false, true, false, false, false, false, false, false);
-            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                    "",  // activity name
-                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
-                    UNIMPORTANT_UID,
-                    UNIMPORTANT_UID2,
-                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
-                    true, // opt in
-                    false, // but no explicit opt in
-                    BackgroundActivityStartController.BAL_BLOCK,
-                    true, // opt in
-                    false // but no explicit opt in
-            ));
-        } finally {
-            mockingSession.finishMocking();
-        }
-    }
-
-    /**
-     * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT, when the PendingIntent sender
-     * is the only reason BAL is allowed.
-     */
-    @Test
-    @RequiresFlagsDisabled(com.android.window.flags.Flags.FLAG_BAL_IMPROVED_METRICS)
-    public void testBackgroundActivityStartsAllowed_loggingOnlyPendingIntentAllowed() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        MockitoSession mockingSession = mockitoSession()
-                .mockStatic(ActivityTaskManagerService.class)
-                .mockStatic(FrameworkStatsLog.class)
-                .mockStatic(PendingIntentRecord.class)
-                .strictness(Strictness.LENIENT)
-                .startMocking();
-        try {
-            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
-                    eq(START_ACTIVITIES_FROM_BACKGROUND),
-                    anyInt(), anyInt()));
-            doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when(
-                    () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
-                            anyObject(), anyInt(), anyObject()));
-            runAndVerifyBackgroundActivityStartsSubtest(
-                    "allowed_notAborted", false,
-                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                    Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP,
-                    false, true, false, false, false, false, false, false,
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED);
-            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                    DEFAULT_COMPONENT_PACKAGE_NAME + "/" + DEFAULT_COMPONENT_PACKAGE_NAME,
-                    BackgroundActivityStartController.BAL_ALLOW_PENDING_INTENT,
-                    UNIMPORTANT_UID,
-                    Process.SYSTEM_UID,
-                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
-                    false, // opt in
-                    true, // explicit opt out
-                    BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW,
-                    true, // opt in
-                    false // but no explicit opt in
-            ));
-        } finally {
-            mockingSession.finishMocking();
-        }
-    }
-
-    /**
-     * This test ensures proper logging for BAL_ALLOW_PENDING_INTENT, when the PendingIntent sender
-     * is not the primary reason to allow BAL (but the creator).
-     */
-    @Test
-    @RequiresFlagsDisabled(com.android.window.flags.Flags.FLAG_BAL_IMPROVED_METRICS)
-    public void testBackgroundActivityStartsAllowed_loggingPendingIntentAllowed() {
-        doReturn(false).when(mAtm).isBackgroundActivityStartsEnabled();
-        MockitoSession mockingSession = mockitoSession()
-                .mockStatic(ActivityTaskManagerService.class)
-                .mockStatic(FrameworkStatsLog.class)
-                .mockStatic(PendingIntentRecord.class)
-                .strictness(Strictness.LENIENT)
-                .startMocking();
-        try {
-            doReturn(PERMISSION_GRANTED).when(() -> ActivityTaskManagerService.checkPermission(
-                    eq(START_ACTIVITIES_FROM_BACKGROUND),
-                    anyInt(), anyInt()));
-            doReturn(BackgroundStartPrivileges.allowBackgroundActivityStarts(null)).when(
-                    () -> PendingIntentRecord.getBackgroundStartPrivilegesAllowedByCaller(
-                            anyObject(), anyInt(), anyObject()));
-            runAndVerifyBackgroundActivityStartsSubtest(
-                    "allowed_notAborted", false,
-                    UNIMPORTANT_UID, false, PROCESS_STATE_BOUND_TOP,
-                    Process.SYSTEM_UID, true, PROCESS_STATE_BOUND_TOP,
-                    false, true, false, false, false, false, false, false,
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
-            verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.BAL_ALLOWED,
-                    "",
-                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
-                    UNIMPORTANT_UID,
-                    Process.SYSTEM_UID,
-                    BackgroundActivityStartController.BAL_ALLOW_PERMISSION,
-                    true, // opt in
-                    true, // explicit opt in
-                    BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW,
-                    true, // opt in
-                    false // but no explicit opt in
-            ));
-        } finally {
-            mockingSession.finishMocking();
-        }
-    }
-
-    private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
-            int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
-            int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
-            boolean hasForegroundActivities, boolean callerIsRecents,
-            boolean callerIsTempAllowed,
-            boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges,
-            boolean isCallingUidDeviceOwner,
-            boolean isCallingUidAffiliatedProfileOwner,
-            boolean isPinnedSingleInstance,
-            boolean hasSystemExemptAppOp) {
-        runAndVerifyBackgroundActivityStartsSubtest(name, shouldHaveAborted, callingUid,
-                callingUidHasVisibleWindow, callingUidProcState, realCallingUid,
-                realCallingUidHasVisibleWindow,  realCallingUidProcState, hasForegroundActivities,
-                callerIsRecents, callerIsTempAllowed,
-                callerIsInstrumentingWithBackgroundActivityStartPrivileges,
-                isCallingUidDeviceOwner, isCallingUidAffiliatedProfileOwner, isPinnedSingleInstance,
-                hasSystemExemptAppOp,
-                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
-    }
-
-    private void runAndVerifyBackgroundActivityStartsSubtest(String name, boolean shouldHaveAborted,
-            int callingUid, boolean callingUidHasVisibleWindow, int callingUidProcState,
-            int realCallingUid, boolean realCallingUidHasVisibleWindow, int realCallingUidProcState,
-            boolean hasForegroundActivities, boolean callerIsRecents,
-            boolean callerIsTempAllowed,
-            boolean callerIsInstrumentingWithBackgroundActivityStartPrivileges,
-            boolean isCallingUidDeviceOwner,
-            boolean isCallingUidAffiliatedProfileOwner,
-            boolean isPinnedSingleInstance,
-            boolean hasSystemExemptAppOp,
-            @BackgroundActivityStartMode int pendingIntentCreatorBackgroundActivityStartMode) {
         // window visibility
-        doReturn(callingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(callingUid);
-        doReturn(realCallingUidHasVisibleWindow).when(mAtm).hasActiveVisibleWindow(realCallingUid);
+        doReturn(false).when(mAtm).hasActiveVisibleWindow(UNIMPORTANT_UID);
+        doReturn(false).when(mAtm).hasActiveVisibleWindow(UNIMPORTANT_UID2);
         // process importance
-        mAtm.mActiveUids.onUidActive(callingUid, callingUidProcState);
-        mAtm.mActiveUids.onUidActive(realCallingUid, realCallingUidProcState);
+        mAtm.mActiveUids.onUidActive(UNIMPORTANT_UID, PROCESS_STATE_BOUND_TOP);
+        mAtm.mActiveUids.onUidActive(UNIMPORTANT_UID2, PROCESS_STATE_BOUND_TOP);
         // foreground activities
         final IApplicationThread caller = mock(IApplicationThread.class);
         final WindowProcessListener listener = mock(WindowProcessListener.class);
         final ApplicationInfo ai = new ApplicationInfo();
-        ai.uid = callingUid;
+        ai.uid = UNIMPORTANT_UID;
         ai.packageName = "com.android.test.package";
-        final WindowProcessController callerApp =
-                spy(new WindowProcessController(mAtm, ai, null, callingUid, -1, null, listener));
-        doReturn(hasForegroundActivities).when(callerApp).hasForegroundActivities();
+        final WindowProcessController callerApp = spy(new WindowProcessController(
+                mAtm, ai, null, UNIMPORTANT_UID, -1, null, listener));
+        doReturn(false).when(callerApp).hasForegroundActivities();
         doReturn(callerApp).when(mAtm).getProcessController(caller);
         // caller is recents
         RecentTasks recentTasks = mock(RecentTasks.class);
         mAtm.mTaskSupervisor.setRecentTasks(recentTasks);
-        doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
+        doReturn(false).when(recentTasks).isCallerRecents(UNIMPORTANT_UID);
         // caller is temp allowed
-        if (callerIsTempAllowed) {
-            callerApp.addOrUpdateBackgroundStartPrivileges(new Binder(),
-                    BackgroundStartPrivileges.ALLOW_BAL);
-        }
         // caller is instrumenting with background activity starts privileges
-        callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
-                callerIsInstrumentingWithBackgroundActivityStartPrivileges ? Process.SHELL_UID : -1,
-                callerIsInstrumentingWithBackgroundActivityStartPrivileges);
+        callerApp.setInstrumenting(false, -1, false);
         // callingUid is the device owner
-        doReturn(isCallingUidDeviceOwner).when(mAtm).isDeviceOwner(callingUid);
+        doReturn(false).when(mAtm).isDeviceOwner(UNIMPORTANT_UID);
         // callingUid is the affiliated profile owner
-        doReturn(isCallingUidAffiliatedProfileOwner).when(mAtm)
-            .isAffiliatedProfileOwner(callingUid);
+        doReturn(false).when(mAtm).isAffiliatedProfileOwner(UNIMPORTANT_UID);
 
         // caller has OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop
-        doReturn(hasSystemExemptAppOp ? AppOpsManager.MODE_ALLOWED
-                : AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).checkOpNoThrow(
+        doReturn(AppOpsManager.MODE_DEFAULT).when(mAppOpsManager).checkOpNoThrow(
                 eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
                 anyInt(), any());
 
-        int launchMode = LAUNCH_MULTIPLE;
-        if (isPinnedSingleInstance) {
-            final ActivityRecord baseActivity =
-                    new ActivityBuilder(mAtm).setCreateTask(true).build();
-            baseActivity.getRootTask()
-                    .setWindowingMode(WINDOWING_MODE_PINNED);
-            doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any());
-            launchMode = LAUNCH_SINGLE_INSTANCE;
-        }
+        final ActivityRecord baseActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        baseActivity.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED);
+        doReturn(baseActivity).when(mRootWindowContainer).findTask(any(), any(), anyBoolean());
 
         ActivityOptions rawOptions = ActivityOptions.makeBasic()
                 .setPendingIntentCreatorBackgroundActivityStartMode(
-                        pendingIntentCreatorBackgroundActivityStartMode);
+                        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
         final ActivityOptions options = spy(rawOptions);
         ActivityRecord[] outActivity = new ActivityRecord[1];
         ActivityStarter starter = prepareStarter(
-                FLAG_ACTIVITY_NEW_TASK, true, launchMode)
+                FLAG_ACTIVITY_NEW_TASK, true, LAUNCH_SINGLE_INSTANCE)
                 .setCallingPackage("com.whatever.dude")
                 .setCaller(caller)
-                .setCallingUid(callingUid)
-                .setRealCallingUid(realCallingUid)
+                .setCallingUid(UNIMPORTANT_UID)
+                .setRealCallingUid(UNIMPORTANT_UID2)
                 .setActivityOptions(new SafeActivityOptions(options))
                 .setOutActivity(outActivity);
 
-        final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
-
-        assertEquals(ActivityStarter.getExternalResult(
-                shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
-        verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+        final int result = starter.setReason("testPinnedSingleInstanceAborted").execute();
+        assertEquals(ActivityStarter.getExternalResult(START_ABORTED), result);
+        verify(options, times(1)).abort();
 
         final ActivityRecord startedActivity = outActivity[0];
         if (startedActivity != null && startedActivity.getTask() != null) {
@@ -2091,6 +1654,120 @@
         assertNotEquals(inTask, target.getTask());
     }
 
+    @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+    @Test
+    public void launchActivity_reusesBubbledTask() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord bubbledActivity = createBubbledActivity();
+
+        // create the target activity to be launched with the same component as the bubbled activity
+        final ActivityRecord targetRecord = new ActivityBuilder(mAtm)
+                .setLaunchMode(LAUNCH_SINGLE_TASK)
+                .setComponent(ActivityBuilder.getDefaultComponent()).build();
+        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+        startActivityInner(starter, targetRecord, bubbledActivity, null /* options */,
+                null /* inTask */, null /* inTaskFragment */);
+
+        assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+    }
+
+    @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+    @Test
+    public void launchActivity_nullSourceRecord_doesNotReuseBubbledTask() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord bubbledActivity = createBubbledActivity();
+
+        // create the target activity to be launched
+        final ActivityRecord targetRecord =
+                new ActivityBuilder(mAtm)
+                        .setLaunchMode(LAUNCH_SINGLE_TASK)
+                        .setComponent(ActivityBuilder.getDefaultComponent()).build();
+        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+        // pass null as the source record
+        startActivityInner(starter, targetRecord, null, null /* options */,
+                null /* inTask */, null /* inTaskFragment */);
+
+        assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask());
+    }
+
+    @EnableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+    @Test
+    public void launchActivity_nonBubbledSourceRecord_doesNotReuseBubbledTask() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord bubbledActivity = createBubbledActivity();
+
+        // create a non bubbled activity
+        final ActivityRecord nonBubbleSourceRecord =
+                new ActivityBuilder(mAtm).setCreateTask(true)
+                        .setLaunchMode(LAUNCH_SINGLE_TASK)
+                        .setComponent(ActivityBuilder.getDefaultComponent())
+                        .build();
+
+        // create the target activity to be launched
+        final ActivityRecord targetRecord =
+                new ActivityBuilder(mAtm)
+                        .setLaunchMode(LAUNCH_SINGLE_TASK)
+                        .setComponent(ActivityBuilder.getDefaultComponent()).build();
+        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+        // use the non bubbled activity as the source
+        startActivityInner(starter, targetRecord, nonBubbleSourceRecord, null /* options */,
+                null /* inTask */, null /* inTaskFragment*/);
+
+        assertNotEquals(bubbledActivity.getTask(), targetRecord.getTask());
+    }
+
+    @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+    @Test
+    public void launchActivity_nullSourceRecord_flagDisabled_reusesBubbledTask() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord bubbledActivity = createBubbledActivity();
+
+        // create the target activity to be launched
+        final ActivityRecord targetRecord =
+                new ActivityBuilder(mAtm)
+                        .setLaunchMode(LAUNCH_SINGLE_TASK)
+                        .setComponent(ActivityBuilder.getDefaultComponent()).build();
+        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+
+        // pass null as the source record
+        startActivityInner(starter, targetRecord, null, null /* options */,
+                null /* inTask */, null /* inTaskFragment */);
+
+        assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+    }
+
+    @DisableFlags(Flags.FLAG_ONLY_REUSE_BUBBLED_TASK_WHEN_LAUNCHED_FROM_BUBBLE)
+    @Test
+    public void launchActivity_fromBubble_flagDisabled_reusesBubbledTask() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord bubbledActivity = createBubbledActivity();
+
+        // create the target activity to be launched with the same component as the bubbled activity
+        final ActivityRecord targetRecord =
+                new ActivityBuilder(mAtm)
+                        .setLaunchMode(LAUNCH_SINGLE_TASK)
+                        .setComponent(ActivityBuilder.getDefaultComponent()).build();
+        starter.getIntent().setComponent(bubbledActivity.mActivityComponent);
+        startActivityInner(starter, targetRecord, bubbledActivity, null /* options */,
+                null /* inTask */, null /* inTaskFragment */);
+
+        assertEquals(bubbledActivity.getTask(), targetRecord.getTask());
+    }
+
+    private ActivityRecord createBubbledActivity() {
+        final ActivityOptions opts = ActivityOptions.makeBasic();
+        opts.setTaskAlwaysOnTop(true);
+        opts.setLaunchedFromBubble(true);
+        opts.setLaunchBounds(new Rect(10, 10, 100, 100));
+        return new ActivityBuilder(mAtm)
+                .setCreateTask(true)
+                .setComponent(ActivityBuilder.getDefaultComponent())
+                .setActivityOptions(opts)
+                .build();
+    }
+
     private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
             ActivityRecord source, ActivityOptions options, Task inTask,
             TaskFragment inTaskFragment) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index a39a1a8..c67d1ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -550,7 +550,7 @@
         }).when(appWindow.mSession).setOnBackInvokedCallbackInfo(eq(appWindow.mClient), any());
 
         addToWindowMap(appWindow, true);
-        dispatcher.attachToWindow(appWindow.mSession, appWindow.mClient, null);
+        dispatcher.attachToWindow(appWindow.mSession, appWindow.mClient, null, null);
 
 
         OnBackInvokedCallback appCallback = createBackCallback(appLatch);
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 4afc8ac..366e519 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -29,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityOptions;
@@ -564,4 +565,136 @@
         assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
                 BAL_ALLOW_SAW_PERMISSION);
     }
+
+    @Test
+    public void testCaller_isRecents() {
+        int callingUid = REGULAR_UID_1;
+        int callingPid = REGULAR_PID_1;
+        final String callingPackage = REGULAR_PACKAGE_1;
+        int realCallingUid = REGULAR_UID_2;
+        int realCallingPid = REGULAR_PID_2;
+
+        // setup state
+        //if (mSupervisor.mRecentTasks.isCallerRecents(state.mCallingUid))
+        RecentTasks recentTasks = mock(RecentTasks.class);
+        when(recentTasks.isCallerRecents(eq(callingUid))).thenReturn(true);
+        mSupervisor.mRecentTasks = recentTasks;
+
+        // prepare call
+        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        Intent intent = TEST_INTENT;
+        ActivityOptions checkedOptions = mCheckedOptions;
+        BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+                callingPid, callingPackage, realCallingUid, realCallingPid, null,
+                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                checkedOptions);
+
+        // call
+        BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
+                balState);
+        balState.setResultForCaller(callerVerdict);
+
+        // assertions
+        assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+                BAL_ALLOW_ALLOWLISTED_COMPONENT);
+    }
+
+    @Test
+    public void testCaller_isDeviceOwner() {
+        int callingUid = REGULAR_UID_1;
+        int callingPid = REGULAR_PID_1;
+        final String callingPackage = REGULAR_PACKAGE_1;
+        int realCallingUid = REGULAR_UID_2;
+        int realCallingPid = REGULAR_PID_2;
+
+        // setup state
+        when(mService.isDeviceOwner(eq(callingUid))).thenReturn(true);
+
+        // prepare call
+        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        Intent intent = TEST_INTENT;
+        ActivityOptions checkedOptions = mCheckedOptions;
+        BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+                callingPid, callingPackage, realCallingUid, realCallingPid, null,
+                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                checkedOptions);
+
+        // call
+        BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
+                balState);
+        balState.setResultForCaller(callerVerdict);
+
+        // assertions
+        assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+                BAL_ALLOW_ALLOWLISTED_COMPONENT);
+    }
+
+    @Test
+    public void testCaller_isAffiliatedProfileOwner() {
+        int callingUid = REGULAR_UID_1;
+        int callingPid = REGULAR_PID_1;
+        final String callingPackage = REGULAR_PACKAGE_1;
+        int realCallingUid = REGULAR_UID_2;
+        int realCallingPid = REGULAR_PID_2;
+
+        // setup state
+        when(mService.isAffiliatedProfileOwner(eq(callingUid))).thenReturn(true);
+
+        // prepare call
+        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        Intent intent = TEST_INTENT;
+        ActivityOptions checkedOptions = mCheckedOptions;
+        BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+                callingPid, callingPackage, realCallingUid, realCallingPid, null,
+                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                checkedOptions);
+
+        // call
+        BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
+                balState);
+        balState.setResultForCaller(callerVerdict);
+
+        // assertions
+        assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+                BAL_ALLOW_ALLOWLISTED_COMPONENT);
+    }
+
+    @Test
+    public void testCaller_isExemptFromBgStartRestriction() {
+        int callingUid = REGULAR_UID_1;
+        int callingPid = REGULAR_PID_1;
+        final String callingPackage = REGULAR_PACKAGE_1;
+        int realCallingUid = REGULAR_UID_2;
+        int realCallingPid = REGULAR_PID_2;
+
+        mDeviceConfig.set("system_exempt_from_activity_bg_start_restriction_enabled", "true");
+        AppOpsManager appOpsManager = mock(AppOpsManager.class);
+        when(mService.getAppOpsManager()).thenReturn(appOpsManager);
+        when(appOpsManager.checkOpNoThrow(eq(
+                        AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
+                eq(callingUid), eq(callingPackage))).thenReturn(AppOpsManager.MODE_ALLOWED);
+
+
+        // prepare call
+        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        Intent intent = TEST_INTENT;
+        ActivityOptions checkedOptions = mCheckedOptions;
+        BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+                callingPid, callingPackage, realCallingUid, realCallingPid, null,
+                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                checkedOptions);
+
+        // call
+        BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
+                balState);
+        balState.setResultForCaller(callerVerdict);
+
+        // assertions
+        assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+                BAL_ALLOW_PERMISSION);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
new file mode 100644
index 0000000..a4df034
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
@@ -0,0 +1,189 @@
+/*
+ * 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.server.wm;
+
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
+import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_DISALLOW;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_GRACE_PERIOD;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.BackgroundStartPrivileges;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests for the {@link BackgroundLaunchProcessController} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:BackgroundLaunchProcessControllerTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class BackgroundLaunchProcessControllerTests {
+
+    Set<IBinder> mActivityStartAllowed = new HashSet<>();
+    Set<Integer> mHasActiveVisibleWindow = new HashSet<>();
+
+    BackgroundActivityStartCallback mCallback = new BackgroundActivityStartCallback() {
+        @Override
+        public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid,
+                String packageName) {
+            for (IBinder token : tokens) {
+                if (token == null || mActivityStartAllowed.contains(token)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean canCloseSystemDialogs(Collection<IBinder> tokens, int uid) {
+            return false;
+        }
+    };
+    BackgroundLaunchProcessController mController = new BackgroundLaunchProcessController(
+            mHasActiveVisibleWindow::contains, mCallback);
+
+    int mPid = 123;
+    int mUid = 234;
+    String mPackageName = "package.name";
+    int mAppSwitchState = APP_SWITCH_DISALLOW;
+    boolean mIsCheckingForFgsStart = false;
+    boolean mHasActivityInVisibleTask = false;
+    boolean mHasBackgroundActivityStartPrivileges = false;
+    long mLastStopAppSwitchesTime = 0L;
+    long mLastActivityLaunchTime = 0L;
+    long mLastActivityFinishTime = 0L;
+
+    @Test
+    public void testNothingAllows() {
+        BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
+                mPid, mUid, mPackageName,
+                mAppSwitchState, mIsCheckingForFgsStart,
+                mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
+                mLastStopAppSwitchesTime, mLastActivityLaunchTime,
+                mLastActivityFinishTime);
+        assertThat(balVerdict.getCode()).isEqualTo(BAL_BLOCK);
+    }
+
+    @Test
+    public void testInstrumenting() {
+        mHasBackgroundActivityStartPrivileges = true;
+        BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
+                mPid, mUid, mPackageName,
+                mAppSwitchState, mIsCheckingForFgsStart,
+                mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
+                mLastStopAppSwitchesTime, mLastActivityLaunchTime,
+                mLastActivityFinishTime);
+        assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_PERMISSION);
+    }
+
+    @Test
+    public void testAllowedByTokenNoCallback() {
+        mController = new BackgroundLaunchProcessController(mHasActiveVisibleWindow::contains,
+                null);
+        Binder token = new Binder();
+        mActivityStartAllowed.add(token);
+        mController.addOrUpdateAllowBackgroundStartPrivileges(token,
+                BackgroundStartPrivileges.ALLOW_BAL);
+        BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
+                mPid, mUid, mPackageName,
+                mAppSwitchState, mIsCheckingForFgsStart,
+                mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
+                mLastStopAppSwitchesTime, mLastActivityLaunchTime,
+                mLastActivityFinishTime);
+        assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_PERMISSION);
+    }
+
+    @Test
+    public void testAllowedByToken() {
+        Binder token = new Binder();
+        mActivityStartAllowed.add(token);
+        mController.addOrUpdateAllowBackgroundStartPrivileges(token,
+                BackgroundStartPrivileges.ALLOW_BAL);
+        BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
+                mPid, mUid, mPackageName,
+                mAppSwitchState, mIsCheckingForFgsStart,
+                mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
+                mLastStopAppSwitchesTime, mLastActivityLaunchTime,
+                mLastActivityFinishTime);
+        assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_PERMISSION);
+    }
+
+    @Test
+    public void testBoundByForeground() {
+        mAppSwitchState = APP_SWITCH_ALLOW;
+        mController.addBoundClientUid(999, "visible.package", Context.BIND_ALLOW_ACTIVITY_STARTS);
+        mHasActiveVisibleWindow.add(999);
+        BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
+                mPid, mUid, mPackageName,
+                mAppSwitchState, mIsCheckingForFgsStart,
+                mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
+                mLastStopAppSwitchesTime, mLastActivityLaunchTime,
+                mLastActivityFinishTime);
+        assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_VISIBLE_WINDOW);
+    }
+
+    @Test
+    public void testForegroundTask() {
+        mAppSwitchState = APP_SWITCH_ALLOW;
+        mHasActivityInVisibleTask = true;
+        BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
+                mPid, mUid, mPackageName,
+                mAppSwitchState, mIsCheckingForFgsStart,
+                mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
+                mLastStopAppSwitchesTime, mLastActivityLaunchTime,
+                mLastActivityFinishTime);
+        assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_FOREGROUND);
+    }
+
+    @Test
+    public void testGracePeriod() {
+        mAppSwitchState = APP_SWITCH_ALLOW;
+        long now = System.currentTimeMillis();
+        mLastStopAppSwitchesTime = now - 10000;
+        mLastActivityLaunchTime = now - 9000;
+        mLastActivityFinishTime = now - 100;
+        BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
+                mPid, mUid, mPackageName,
+                mAppSwitchState, mIsCheckingForFgsStart,
+                mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
+                mLastStopAppSwitchesTime, mLastActivityLaunchTime,
+                mLastActivityFinishTime);
+        assertThat(balVerdict.getCode()).isEqualTo(BAL_ALLOW_GRACE_PERIOD);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
new file mode 100644
index 0000000..b3f1502
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -0,0 +1,357 @@
+/*
+ * 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.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.CameraCompatTaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.RefreshCallbackItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.res.Configuration;
+import android.content.res.Configuration.Orientation;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Tests for {@link CameraCompatFreeformPolicy}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:CameraCompatFreeformPolicyTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    // Main activity package name needs to be the same as the process to test overrides.
+    private static final String TEST_PACKAGE_1 = "com.android.frameworks.wmtests";
+    private static final String TEST_PACKAGE_2 = "com.test.package.two";
+    private static final String CAMERA_ID_1 = "camera-1";
+    private static final String CAMERA_ID_2 = "camera-2";
+    private CameraManager mMockCameraManager;
+    private Handler mMockHandler;
+    private LetterboxConfiguration mLetterboxConfiguration;
+
+    private CameraManager.AvailabilityCallback mCameraAvailabilityCallback;
+    private CameraCompatFreeformPolicy mCameraCompatFreeformPolicy;
+    private ActivityRecord mActivity;
+    private Task mTask;
+    private ActivityRefresher mActivityRefresher;
+
+    @Before
+    public void setUp() throws Exception {
+        mLetterboxConfiguration = mDisplayContent.mWmService.mLetterboxConfiguration;
+        spyOn(mLetterboxConfiguration);
+        when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
+                .thenReturn(true);
+        when(mLetterboxConfiguration.isCameraCompatRefreshEnabled())
+                .thenReturn(true);
+        when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
+                .thenReturn(true);
+
+        mMockCameraManager = mock(CameraManager.class);
+        doAnswer(invocation -> {
+            mCameraAvailabilityCallback = invocation.getArgument(1);
+            return null;
+        }).when(mMockCameraManager).registerAvailabilityCallback(
+                any(Executor.class), any(CameraManager.AvailabilityCallback.class));
+
+        when(mContext.getSystemService(CameraManager.class)).thenReturn(mMockCameraManager);
+
+        mDisplayContent.setIgnoreOrientationRequest(true);
+
+        mMockHandler = mock(Handler.class);
+
+        when(mMockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
+                invocation -> {
+                    ((Runnable) invocation.getArgument(0)).run();
+                    return null;
+                });
+
+        mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler);
+        mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
+        CameraStateMonitor cameraStateMonitor =
+                new CameraStateMonitor(mDisplayContent, mMockHandler);
+        mCameraCompatFreeformPolicy =
+                new CameraCompatFreeformPolicy(mDisplayContent, cameraStateMonitor,
+                        mActivityRefresher);
+
+        mCameraCompatFreeformPolicy.start();
+        cameraStateMonitor.startListeningToCameraState();
+    }
+
+    @Test
+    public void testFullscreen_doesNotActivateCameraCompatMode() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN);
+        doReturn(false).when(mActivity).inFreeformWindowingMode();
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertNotInCameraCompatMode();
+    }
+
+    @Test
+    public void testOrientationUnspecified_doesNotActivateCameraCompatMode() {
+        configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+
+        assertNotInCameraCompatMode();
+    }
+
+    @Test
+    public void testNoCameraConnection_doesNotActivateCameraCompatMode() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        assertNotInCameraCompatMode();
+    }
+
+    @Test
+    public void testCameraConnected_activatesCameraCompatMode() throws Exception {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        assertInCameraCompatMode();
+        assertActivityRefreshRequested(/* refreshRequested */ false);
+    }
+
+    @Test
+    public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        callOnActivityConfigurationChanging(mActivity);
+
+        assertInCameraCompatMode();
+        assertActivityRefreshRequested(/* refreshRequested */ true);
+    }
+
+    @Test
+    public void testReconnectedToDifferentCamera_activatesCameraCompatModeAndRefresh()
+            throws Exception {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_2, TEST_PACKAGE_1);
+        callOnActivityConfigurationChanging(mActivity);
+
+        assertInCameraCompatMode();
+        assertActivityRefreshRequested(/* refreshRequested */ true);
+    }
+
+    @Test
+    public void testCameraDisconnected_deactivatesCameraCompatMode() {
+        configureActivityAndDisplay(SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE,
+                WINDOWING_MODE_FREEFORM);
+        // Open camera and test for compat treatment
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        assertInCameraCompatMode();
+
+        // Close camera and test for revert
+        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
+
+        assertNotInCameraCompatMode();
+    }
+
+    @Test
+    public void testCameraOpenedForDifferentPackage_notInCameraCompatMode() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_2);
+
+        assertNotInCameraCompatMode();
+    }
+
+    @Test
+    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
+    public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        assertTrue(mActivity.info
+                .isChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT));
+        assertFalse(mCameraCompatFreeformPolicy
+                .shouldApplyFreeformTreatmentForCameraCompat(mActivity));
+    }
+
+    @Test
+    public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        assertTrue(mCameraCompatFreeformPolicy
+                .shouldApplyFreeformTreatmentForCameraCompat(mActivity));
+    }
+
+    @Test
+    public void testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh()
+            throws Exception {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        doReturn(false).when(
+                mActivity.mLetterboxUiController).shouldRefreshActivityForCameraCompat();
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        callOnActivityConfigurationChanging(mActivity);
+
+        assertActivityRefreshRequested(/* refreshRequested */ false);
+    }
+
+    @Test
+    public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() throws Exception {
+        when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
+                .thenReturn(false);
+
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        callOnActivityConfigurationChanging(mActivity);
+
+        assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+    }
+
+    @Test
+    public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp()
+            throws Exception {
+        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
+        doReturn(true).when(mActivity.mLetterboxUiController)
+                .shouldRefreshActivityViaPauseForCameraCompat();
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        callOnActivityConfigurationChanging(mActivity);
+
+        assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+    }
+
+    private void configureActivity(@ScreenOrientation int activityOrientation) {
+        configureActivity(activityOrientation, WINDOWING_MODE_FREEFORM);
+    }
+
+    private void configureActivity(@ScreenOrientation int activityOrientation,
+            @WindowingMode int windowingMode) {
+        configureActivityAndDisplay(activityOrientation, ORIENTATION_PORTRAIT, windowingMode);
+    }
+
+    private void configureActivityAndDisplay(@ScreenOrientation int activityOrientation,
+            @Orientation int naturalOrientation, @WindowingMode int windowingMode) {
+        mTask = new TaskBuilder(mSupervisor)
+                .setDisplay(mDisplayContent)
+                .setWindowingMode(windowingMode)
+                .build();
+
+        mActivity = new ActivityBuilder(mAtm)
+                // Set the component to be that of the test class in order to enable compat changes
+                .setComponent(ComponentName.createRelative(mContext,
+                        com.android.server.wm.CameraCompatFreeformPolicyTests.class.getName()))
+                .setScreenOrientation(activityOrientation)
+                .setTask(mTask)
+                .build();
+
+        spyOn(mActivity.mLetterboxUiController);
+        spyOn(mActivity.info);
+
+        doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
+        doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
+
+        doReturn(true).when(mActivity).inFreeformWindowingMode();
+    }
+
+    private void assertInCameraCompatMode() {
+        assertNotEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE,
+                mActivity.mLetterboxUiController.getFreeformCameraCompatMode());
+    }
+
+    private void assertNotInCameraCompatMode() {
+        assertEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE,
+                mActivity.mLetterboxUiController.getFreeformCameraCompatMode());
+    }
+
+    private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
+        assertActivityRefreshRequested(refreshRequested, /* cycleThroughStop*/ true);
+    }
+
+    private void assertActivityRefreshRequested(boolean refreshRequested,
+            boolean cycleThroughStop) throws Exception {
+        verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
+                .setIsRefreshRequested(true);
+
+        final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
+                cycleThroughStop ? ON_STOP : ON_PAUSE);
+        final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token,
+                /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+
+        verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
+                .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(),
+                        refreshCallbackItem, resumeActivityItem);
+    }
+
+    private void callOnActivityConfigurationChanging(ActivityRecord activity) {
+        mActivityRefresher.onActivityConfigurationChanging(activity,
+                /* newConfig */ createConfiguration(/*letterbox=*/ true),
+                /* lastReportedConfig */ createConfiguration(/*letterbox=*/ false));
+    }
+
+    private Configuration createConfiguration(boolean letterbox) {
+        final Configuration configuration = new Configuration();
+        Rect bounds = letterbox ? new Rect(300, 0, 700, 600) : new Rect(0, 0, 1000, 600);
+        configuration.windowConfiguration.setAppBounds(bounds);
+        return configuration;
+    }
+}
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 417ee6b..6957502 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -83,6 +83,8 @@
 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_DESKTOP_WINDOWING_MODE;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -115,6 +117,8 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArraySet;
 import android.view.Display;
@@ -2822,6 +2826,31 @@
         verify(mWm.mUmInternal, never()).isUserVisible(userId2, displayId);
     }
 
+    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @Test
+    public void cameraCompatFreeformFlagEnabled_cameraCompatFreeformPolicyNotNull() {
+        doReturn(true).when(() ->
+                DesktopModeLaunchParamsModifier.canEnterDesktopMode(any()));
+
+        assertNotNull(createNewDisplay().mCameraCompatFreeformPolicy);
+    }
+
+    @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @Test
+    public void cameraCompatFreeformFlagNotEnabled_cameraCompatFreeformPolicyIsNull() {
+        doReturn(true).when(() ->
+                DesktopModeLaunchParamsModifier.canEnterDesktopMode(any()));
+
+        assertNull(createNewDisplay().mCameraCompatFreeformPolicy);
+    }
+
+    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+    @Test
+    public void desktopWindowingFlagNotEnabled_cameraCompatFreeformPolicyIsNull() {
+        assertNull(createNewDisplay().mCameraCompatFreeformPolicy);
+    }
+
     private void removeRootTaskTests(Runnable runnable) {
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index c76acd7..c65371f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -142,6 +142,7 @@
         doNothing().when(mDisplayRotationCompatPolicy).showToast(anyInt());
         doNothing().when(mDisplayRotationCompatPolicy).showToast(anyInt(), anyString());
 
+        mDisplayRotationCompatPolicy.start();
         cameraStateMonitor.startListeningToCameraState();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index c7f5020..cd19449 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -77,6 +77,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
 import android.compat.testing.PlatformCompatChangeRule;
@@ -110,7 +111,7 @@
 import org.junit.runner.RunWith;
 
 /**
- * Test class for {@link LetterboxUiControllerTest}.
+ * Test class for {@link LetterboxUiController}.
  *
  * Build/Install/Run:
  * atest WmTests:LetterboxUiControllerTest
@@ -521,8 +522,8 @@
         final Rect opaqueBounds = new Rect(0, 0, 500, 300);
         doReturn(opaqueBounds).when(mActivity).getBounds();
         // Activity is translucent
-        spyOn(mActivity.mLetterboxUiController);
-        doReturn(true).when(mActivity.mLetterboxUiController).hasInheritedLetterboxBehavior();
+        spyOn(mActivity.mTransparentPolicy);
+        when(mActivity.mTransparentPolicy.isRunning()).thenReturn(true);
 
         // Makes requested sizes different
         mainWindow.mRequestedWidth = opaqueBounds.width() - 1;
@@ -908,7 +909,7 @@
     }
 
     @Test
-    public void testOverrideOrientationIfNeeded_fullscreenOverride_cameraActivity_unchanged() {
+    public void testOverrideOrientationIfNeeded_userFullscreenOverride_cameraActivity_noChange() {
         doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
         doReturn(true).when(mLetterboxConfiguration)
                 .isCameraCompatTreatmentEnabledAtBuildTime();
@@ -916,9 +917,31 @@
         // Recreate DisplayContent with DisplayRotationCompatPolicy
         mActivity = setUpActivityWithComponent();
         mController = new LetterboxUiController(mWm, mActivity);
-        spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
+        spyOn(mController);
+        doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
 
-        doReturn(false).when(mDisplayContent.mDisplayRotationCompatPolicy)
+        spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
+        doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
+                .isCameraActive(mActivity, /* mustBeFullscreen= */ true);
+
+        assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
+                /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+    }
+
+    @Test
+    public void testOverrideOrientationIfNeeded_systemFullscreenOverride_cameraActivity_noChange() {
+        doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
+        doReturn(true).when(mLetterboxConfiguration)
+                .isCameraCompatTreatmentEnabledAtBuildTime();
+
+        // Recreate DisplayContent with DisplayRotationCompatPolicy
+        mActivity = setUpActivityWithComponent();
+        mController = new LetterboxUiController(mWm, mActivity);
+        spyOn(mController);
+        doReturn(true).when(mController).isSystemOverrideToFullscreenEnabled();
+
+        spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
+        doReturn(true).when(mDisplayContent.mDisplayRotationCompatPolicy)
                 .isCameraActive(mActivity, /* mustBeFullscreen= */ true);
 
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
@@ -1056,7 +1079,7 @@
     }
 
     @Test
-    public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientaion_returnsFalse()
+    public void testShouldEnableUserAspectRatioSettings_noIgnoreOrientation_returnsFalse()
             throws Exception {
         prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
         mockThatProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, /* value */ true);
@@ -1104,7 +1127,7 @@
     }
 
     @Test
-    public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientationreturnsFalse() {
+    public void testShouldApplyUserMinAspectRatioOverride_noIgnoreOrientation_returnsFalse() {
         prepareActivityForShouldApplyUserMinAspectRatioOverride(/* orientationRequest */ false);
 
         assertFalse(mController.shouldApplyUserMinAspectRatioOverride());
diff --git a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
index 8cf2776..3e87f1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PersisterQueueTests.java
@@ -125,7 +125,7 @@
                 processDuration < PRE_TASK_DELAY_MS);
 
         assertTrue("Target didn't call callback enough times.",
-                mFactory.waitForAllExpectedItemsProcessed(TIMEOUT_ALLOWANCE));
+                mListener.waitForAllExpectedCallbackDone(TIMEOUT_ALLOWANCE));
         // Once before processing this item, once after that.
         assertEquals(2, mListener.mProbablyDoneResults.size());
         // The last one must be called with probably done being true.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index ce90504..e019a41 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -405,11 +405,12 @@
 
         final RootWindowContainer.FindTaskResult result =
                 new RootWindowContainer.FindTaskResult();
-        result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info);
+        result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info, true);
         result.process(task);
 
-        assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */));
-        assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */));
+        assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */, true));
+        assertEquals(
+                taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */, true));
         assertNotNull(result.mIdealRecord);
     }
 
@@ -432,7 +433,7 @@
         final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent(
                 target).setTargetActivity(targetActivity).build();
         RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult();
-        result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info);
+        result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info, true);
         result.process(parentTask);
         assertThat(result.mIdealRecord).isNotNull();
 
@@ -440,7 +441,7 @@
         final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent(
                 alias).setTargetActivity(targetActivity).build();
         result = new RootWindowContainer.FindTaskResult();
-        result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info);
+        result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info, true);
         result.process(parentTask);
         assertThat(result.mIdealRecord).isNotNull();
     }
@@ -1234,12 +1235,18 @@
         assertEquals(STOPPING, activity2.getState());
         assertThat(mSupervisor.mStoppingActivities).contains(activity2);
 
+        registerTestTransitionPlayer();
+        final Transition transition = display.mTransitionController
+                .requestCloseTransitionIfNeeded(rootTask1);
+        transition.collectClose(rootTask1);
         // The display becomes empty. Since there is no next activity to be idle, the activity
         // should be destroyed immediately with updating configuration to restore original state.
         final ActivityRecord activity1 = finishTopActivity(rootTask1);
         assertEquals(DESTROYING, activity1.getState());
         verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
                 eq(display), anyBoolean());
+        assertTrue("Transition must be ready if there is no next running activity",
+                transition.allReady());
     }
 
     private ActivityRecord finishTopActivity(Task task) {
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 d88871c..eb79118 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -147,6 +147,40 @@
     }
 
     @Test
+    public void testFindTask_includeLaunchedFromBubbled() {
+        final ComponentName component = ComponentName.createRelative(
+                DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity");
+        final ActivityOptions opts = ActivityOptions.makeBasic();
+        opts.setTaskAlwaysOnTop(true);
+        opts.setLaunchedFromBubble(true);
+        final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService)
+                .setComponent(component)
+                .setActivityOptions(opts)
+                .setCreateTask(true)
+                .build();
+
+        assertEquals(activity, mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(),
+                true /* includeLaunchedFromBubble */));
+    }
+
+    @Test
+    public void testFindTask_ignoreLaunchedFromBubbled() {
+        final ComponentName component = ComponentName.createRelative(
+                DEFAULT_COMPONENT_PACKAGE_NAME, ".BubbledActivity");
+        final ActivityOptions opts = ActivityOptions.makeBasic();
+        opts.setTaskAlwaysOnTop(true);
+        opts.setLaunchedFromBubble(true);
+        final ActivityRecord activity = new ActivityBuilder(mWm.mAtmService)
+                .setComponent(component)
+                .setActivityOptions(opts)
+                .setCreateTask(true)
+                .build();
+
+        assertNull(mWm.mRoot.findTask(activity, activity.getTaskDisplayArea(),
+                false /* includeLaunchedFromBubble */));
+    }
+
+    @Test
     public void testAllPausedActivitiesComplete() {
         DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
         ActivityRecord activity = createActivityRecord(displayContent);
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 7ced9d5..ac1aa20 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ROTATION_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;
@@ -62,7 +60,6 @@
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
 import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
-import static com.android.server.wm.ActivityRecord.State.DESTROYED;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
 import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -77,7 +74,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -109,7 +105,6 @@
 import android.os.UserHandle;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
 import android.view.InsetsFrameProvider;
@@ -199,26 +194,6 @@
     }
 
     @Test
-    public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(translucentActivity);
-
-        translucentActivity.setState(DESTROYED, "testing");
-        translucentActivity.removeImmediately();
-
-        assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-    }
-
-    @Test
     public void testHorizontalReachabilityEnabledForTranslucentActivities() {
         testReachabilityEnabledForTranslucentActivity(/* dw */ 2500,  /* dh */1000,
                 SCREEN_ORIENTATION_PORTRAIT, /* minAspectRatio */ 0f,
@@ -364,45 +339,8 @@
     }
 
     @Test
-    public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        // Launch another opaque activity
-        final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(opaqueActivity);
-        // Transparent activity strategy not applied
-        assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-
-        // Launch translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(translucentActivity);
-        // Transparent strategy applied
-        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-
-        spyOn(translucentActivity.mLetterboxUiController);
-        clearInvocations(translucentActivity.mLetterboxUiController);
-
-        // We destroy the first opaque activity
-        opaqueActivity.setState(DESTROYED, "testing");
-        opaqueActivity.removeImmediately();
-
-        // Check that updateInheritedLetterbox() is invoked again
-        verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
-    }
-
-    // TODO(b/333663877): Enable test after fix
-    @Test
-    @RequiresFlagsDisabled({Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION})
     @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING)
+    @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testRepositionLandscapeImmersiveAppWithDisplayCutout() {
         final int dw = 2100;
         final int dh = 2000;
@@ -416,11 +354,14 @@
         mWm.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
         mWm.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
 
-        doReturn(true).when(mActivity).isImmersiveMode(any());
-        prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
-                SCREEN_ORIENTATION_LANDSCAPE);
-        addWindowToActivity(mActivity);
-        mActivity.mRootWindowContainer.performSurfacePlacement();
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
+                .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
+                .build();
+
+        doReturn(true).when(activity).isImmersiveMode(any());
+        addWindowToActivity(activity);
+        activity.mRootWindowContainer.performSurfacePlacement();
 
         final Function<ActivityRecord, Rect> innerBoundsOf =
                 (ActivityRecord a) -> {
@@ -431,299 +372,22 @@
 
         final Consumer<Integer> doubleClick =
                 (Integer y) -> {
-                    mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
-                    mActivity.mRootWindowContainer.performSurfacePlacement();
+                    activity.mLetterboxUiController.handleVerticalDoubleTap(y);
+                    activity.mRootWindowContainer.performSurfacePlacement();
                 };
 
-        final Rect bounds = mActivity.getBounds();
+        final Rect bounds = activity.getBounds();
         assertTrue(bounds.top > cutoutHeight && bounds.bottom < dh);
         assertEquals(dw, bounds.width());
 
         // Double click bottom.
         doubleClick.accept(dh - 10);
-        assertEquals(dh, innerBoundsOf.apply(mActivity).bottom);
+        assertEquals(dh, innerBoundsOf.apply(activity).bottom);
 
         // Double click top.
         doubleClick.accept(10);
         doubleClick.accept(10);
-        assertEquals(cutoutHeight, innerBoundsOf.apply(mActivity).top);
-    }
-
-    @Test
-    public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-
-        // Launch translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(translucentActivity);
-        // Transparent strategy applied
-        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-        assertNotNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
-
-        spyOn(translucentActivity.mLetterboxUiController);
-        clearInvocations(translucentActivity.mLetterboxUiController);
-
-        // We destroy the first opaque activity
-        mActivity.removeImmediately();
-
-        // Check that updateInheritedLetterbox() is invoked again
-        verify(translucentActivity.mLetterboxUiController).updateInheritedLetterbox();
-        assertNull(translucentActivity.mLetterboxUiController.mFirstOpaqueActivityBeneath);
-    }
-
-    @Test
-    public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        // Launch another opaque activity
-        final ActivityRecord opaqueActivity = new ActivityBuilder(mAtm)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(opaqueActivity);
-        // Transparent activity strategy not applied
-        assertFalse(opaqueActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-
-        // Launch translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(translucentActivity);
-        // Transparent strategy applied
-        assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-
-        spyOn(translucentActivity.mLetterboxUiController);
-        clearInvocations(translucentActivity.mLetterboxUiController);
-
-        // Check that updateInheritedLetterbox() is invoked again
-        verify(translucentActivity.mLetterboxUiController, never()).updateInheritedLetterbox();
-    }
-
-    @Test
-    public void testApplyStrategyToTranslucentActivities() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.info.setMinAspectRatio(1.2f);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
-                .setMinAspectRatio(1.1f)
-                .setMaxAspectRatio(3f)
-                .build();
-        mTask.addChild(translucentActivity);
-        // We check bounds
-        final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
-        final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
-        assertEquals(opaqueBounds, translucentRequestedBounds);
-        // We check orientation
-        final int translucentOrientation =
-                translucentActivity.getRequestedConfigurationOrientation();
-        assertEquals(ORIENTATION_PORTRAIT, translucentOrientation);
-        // We check aspect ratios
-        assertEquals(1.2f, translucentActivity.getMinAspectRatio(), 0.00001f);
-        assertEquals(1.5f, translucentActivity.getMaxAspectRatio(), 0.00001f);
-    }
-
-    @Test
-    public void testApplyStrategyToTranslucentActivitiesRetainsWindowConfigurationProperties() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .build();
-        final Configuration requestedConfig =
-                translucentActivity.getRequestedOverrideConfiguration();
-        final WindowConfiguration translucentWinConf = requestedConfig.windowConfiguration;
-        translucentWinConf.setActivityType(ACTIVITY_TYPE_STANDARD);
-        translucentWinConf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        translucentWinConf.setAlwaysOnTop(true);
-        translucentActivity.onRequestedOverrideConfigurationChanged(requestedConfig);
-
-        mTask.addChild(translucentActivity);
-
-        // The original override of WindowConfiguration should keep.
-        assertEquals(ACTIVITY_TYPE_STANDARD, translucentActivity.getActivityType());
-        assertEquals(WINDOWING_MODE_MULTI_WINDOW, translucentWinConf.getWindowingMode());
-        assertTrue(translucentWinConf.isAlwaysOnTop());
-        // Unless display is going to be rotated, it should always inherit from parent.
-        assertEquals(ROTATION_UNDEFINED, translucentWinConf.getDisplayRotation());
-    }
-
-    @Test
-    public void testApplyStrategyToMultipleTranslucentActivities() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.info.setMinAspectRatio(1.2f);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
-                .setMinAspectRatio(1.1f)
-                .setMaxAspectRatio(3f)
-                .build();
-        mTask.addChild(translucentActivity);
-        // We check bounds
-        final Rect opaqueBounds = mActivity.getConfiguration().windowConfiguration.getBounds();
-        final Rect translucentRequestedBounds = translucentActivity.getRequestedOverrideBounds();
-        assertEquals(opaqueBounds, translucentRequestedBounds);
-        // Launch another translucent activity
-        final ActivityRecord translucentActivity2 = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
-                .build();
-        mTask.addChild(translucentActivity2);
-        // We check bounds
-        final Rect translucent2RequestedBounds = translucentActivity2.getRequestedOverrideBounds();
-        assertEquals(opaqueBounds, translucent2RequestedBounds);
-    }
-
-    @Test
-    public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        // Mock the activity as embedded without additional TaskFragment layer in the task for
-        // simplicity.
-        doReturn(true).when(mActivity).isEmbedded();
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent).build();
-        doReturn(false).when(translucentActivity).matchParentBounds();
-        mTask.addChild(translucentActivity);
-        // Check the strategy has not being applied
-        assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-    }
-
-    @Test
-    public void testTranslucentActivitiesDontGoInSizeCompatMode() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2800, 1400);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        // Rotate to put activity in size compat mode.
-        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
-        assertTrue(mActivity.inSizeCompatMode());
-        // Rotate back
-        rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
-        assertFalse(mActivity.inSizeCompatMode());
-        // We launch a transparent activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(translucentActivity);
-        // It should not be in SCM
-        assertFalse(translucentActivity.inSizeCompatMode());
-        // We rotate again
-        rotateDisplay(translucentActivity.mDisplayContent, ROTATION_90);
-        assertFalse(translucentActivity.inSizeCompatMode());
-    }
-
-    @Test
-    public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2000, 1000);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        // Translucent Activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .build();
-        assertFalse(translucentActivity.fillsParent());
-        assertTrue(mActivity.fillsParent());
-        mActivity.finishing = true;
-        assertFalse(mActivity.occludesParent());
-        mTask.addChild(translucentActivity);
-        // The translucent activity won't inherit letterbox behavior from a finishing activity.
-        assertFalse(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior());
-    }
-
-    @Test
-    public void testTranslucentActivitiesWhenUnfolding() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2800, 1400);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(
-                true /* ignoreOrientationRequest */);
-        mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(
-                1.0f /*letterboxVerticalPositionMultiplier*/);
-        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
-        // We launch a transparent activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(translucentActivity);
-        assertEquals(translucentActivity.getBounds(), mActivity.getBounds());
-
-        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        spyOn(mActivity);
-
-        // Halffold
-        setFoldablePosture(translucentActivity, true /* isHalfFolded */,
-                false /* isTabletop */);
-        verify(mActivity).recomputeConfiguration();
-        assertEquals(translucentActivity.getBounds(), mActivity.getBounds());
-        clearInvocations(mActivity);
-
-        // Unfold
-        setFoldablePosture(translucentActivity, false /* isHalfFolded */,
-                false /* isTabletop */);
-        verify(mActivity).recomputeConfiguration();
-        assertEquals(translucentActivity.getBounds(), mActivity.getBounds());
-    }
-
-    @Test
-    public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared() {
-        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
-        setUpDisplaySizeWithApp(2800, 1400);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
-        prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        // Rotate to put activity in size compat mode.
-        rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
-        assertTrue(mActivity.inSizeCompatMode());
-
-        // We launch a transparent activity
-        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
-                .setActivityTheme(android.R.style.Theme_Translucent)
-                .setLaunchedFromUid(mActivity.getUid())
-                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .build();
-        mTask.addChild(translucentActivity);
-
-        // The transparent activity inherits the compat display insets of the opaque activity
-        // beneath it
-        assertNotNull(translucentActivity.getCompatDisplayInsets());
-
-        // Clearing SCM should also clear the inherited compat display insets
-        translucentActivity.clearSizeCompatMode();
-        assertNull(translucentActivity.getCompatDisplayInsets());
+        assertEquals(cutoutHeight, innerBoundsOf.apply(activity).top);
     }
 
     @Test
@@ -754,26 +418,25 @@
     }
 
     @Test
+    @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testFixedAspectRatioBoundsWithDecorInSquareDisplay() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         final int notchHeight = 100;
         setUpApp(new TestDisplayContent.Builder(mAtm, 600, 800).setNotch(notchHeight).build());
 
         final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
         final float aspectRatio = 1.2f;
-        mActivity.info.setMaxAspectRatio(aspectRatio);
-        mActivity.info.setMinAspectRatio(aspectRatio);
-        prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED);
-        final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
+                .setMinAspectRatio(aspectRatio)
+                .setMaxAspectRatio(aspectRatio)
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .build();
+        final Rect appBounds = activity.getWindowConfiguration().getAppBounds();
 
         // The parent configuration doesn't change since the first resolved configuration, so the
         // activity should fit in the parent naturally (size=583x700, appBounds=[9, 100 - 592, 800],
         // horizontal offset = round((600 - 583) / 2) = 9)).
-        assertFitted();
+        assertFitted(activity);
         final int offsetX = (int) ((1f + displayBounds.width() - appBounds.width()) / 2);
         // The bounds must be horizontal centered.
         assertEquals(offsetX, appBounds.left);
@@ -781,30 +444,30 @@
         // Ensure the app bounds keep the declared aspect ratio.
         assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
         // The decor height should be a part of the effective bounds.
-        assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
+        assertEquals(activity.getBounds().height(), appBounds.height() + notchHeight);
         // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
-        assertActivityMaxBoundsSandboxed();
+        assertActivityMaxBoundsSandboxed(activity);
         // Activity max bounds ignore notch, since an app can be shown past the notch (although app
         // is currently limited by the notch).
-        assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+        assertThat(activity.getWindowConfiguration().getMaxBounds().height())
                 .isEqualTo(displayBounds.height());
 
-        mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
-        assertFitted();
+        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertFitted(activity);
 
         // After the orientation of activity is changed, the display is rotated, the aspect
         // ratio should be the same (bounds=[0, 0 - 800, 583], appBounds=[100, 0 - 800, 583]).
         assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
         // Activity max bounds are sandboxed.
-        assertActivityMaxBoundsSandboxed();
+        assertActivityMaxBoundsSandboxed(activity);
 
-        mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-        assertFitted();
+        activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+        assertFitted(activity);
         // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
-        assertActivityMaxBoundsSandboxed();
+        assertActivityMaxBoundsSandboxed(activity);
         // Activity max bounds ignore notch, since an app can be shown past the notch (although app
         // is currently limited by the notch).
-        assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
+        assertThat(activity.getWindowConfiguration().getMaxBounds().height())
                 .isEqualTo(displayBounds.height());
     }
 
@@ -1011,12 +674,8 @@
 
     @Test
     public void testMoveToDifferentOrientationDisplay() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         setUpDisplaySizeWithApp(1000, 2500);
+        mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
 
@@ -1063,16 +722,12 @@
 
     @Test
     public void testFixedOrientationRotateCutoutDisplay() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         // Create a display with a notch/cutout
         final int notchHeight = 60;
         final int width = 1000;
         setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
                 .setNotch(notchHeight).build());
+        mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
         // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
         final float maxAspect = 1.4f;
         prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -1665,12 +1320,8 @@
         setUpDisplaySizeWithApp(1000, 1200);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override forces the activity into a 3:2 aspect ratio
@@ -1682,24 +1333,18 @@
     @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testOverrideMinAspectRatioLowerThanManifest() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
-        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, 1800)
-                .setNotch(200).setSystemDecorations(true).build();
+        final int dh = 1800;
+        final int notchHeight = 200;
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1400, dh)
+                .setNotch(notchHeight).setSystemDecorations(true).build();
         mTask = new TaskBuilder(mSupervisor).setDisplay(display).build();
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
                 .setMinAspectRatio(2f)
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override should have no effect, because the manifest aspect ratio is
@@ -1708,7 +1353,7 @@
         assertEquals("App bounds must have min aspect ratio", 2f,
                 (float) appBounds.height() / appBounds.width(), 0.0001f /* delta */);
         assertEquals("Long side must fit task",
-                mTask.getWindowConfiguration().getAppBounds().height(), appBounds.height());
+                dh - notchHeight, appBounds.height());
         assertEquals("Bounds can include insets", mTask.getBounds().height(),
                 activity.getBounds().height());
     }
@@ -1720,13 +1365,9 @@
         setUpDisplaySizeWithApp(1400, 1600);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
                 .setMinAspectRatio(1.1f)
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override should have no effect, because the manifest aspect ratio is
@@ -1743,12 +1384,8 @@
         setUpDisplaySizeWithApp(1500, 1600);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override forces the activity into a 16:9 aspect ratio
@@ -1767,12 +1404,8 @@
         setUpDisplaySizeWithApp(1400, 1600);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override forces the activity into a 16:9 aspect ratio
@@ -1792,12 +1425,7 @@
         setUpDisplaySizeWithApp(1000, 1200);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
-                .build();
+        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1824,12 +1452,8 @@
         setUpDisplaySizeWithApp(1000, 1200);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override should have no effect
@@ -1854,12 +1478,8 @@
         setUpDisplaySizeWithApp(1000, 1200);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override forces the activity into a 3:2 aspect ratio
@@ -1884,12 +1504,7 @@
         setUpDisplaySizeWithApp(1000, 1200);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
-                .build();
+        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1908,12 +1523,8 @@
         setUpDisplaySizeWithApp(1000, 1200);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override forces the activity into a 3:2 aspect ratio
@@ -1931,12 +1542,8 @@
         setUpDisplaySizeWithApp(1000, 1200);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // The per-package override should have no effect
@@ -1951,12 +1558,8 @@
         setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         final TestSplitOrganizer organizer =
@@ -2034,15 +1637,11 @@
 
     @Test
     public void testLaunchWithFixedRotationTransform() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         final int dw = 1000;
         final int dh = 2500;
         final int notchHeight = 200;
         setUpApp(new TestDisplayContent.Builder(mAtm, dw, dh).setNotch(notchHeight).build());
+        mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
         // The test assumes the notch will be at left side when the orientation is landscape.
         if (mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_reverseDefaultRotation)) {
@@ -2652,12 +2251,7 @@
     private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation,
             float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio,
             boolean enabled) {
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
-                .build();
+        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         spyOn(activity.mWmService.mLetterboxConfiguration);
         doReturn(enabled).when(activity.mWmService.mLetterboxConfiguration)
@@ -2688,12 +2282,8 @@
         final int displayWidth = 1400;
         final int displayHeight = 1600;
         setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setMinAspectRatio(1.1f)
-                .setUid(android.os.Process.myUid())
                 .build();
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2713,12 +2303,8 @@
         final int displayWidth = 1600;
         final int displayHeight = 1400;
         setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setMinAspectRatio(1.1f)
-                .setUid(android.os.Process.myUid())
                 .build();
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2739,12 +2325,8 @@
         final int displayWidth = 1400;
         final int displayHeight = 1600;
         setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setMinAspectRatio(1.1f)
-                .setUid(android.os.Process.myUid())
                 .build();
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2765,12 +2347,8 @@
         final int displayWidth = 1600;
         final int displayHeight = 1400;
         setUpDisplaySizeWithApp(displayWidth, displayHeight);
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setMinAspectRatio(1.1f)
-                .setUid(android.os.Process.myUid())
                 .build();
         // Setup Letterbox Configuration
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -2791,12 +2369,7 @@
         final int screenWidth = 1800;
         final int screenHeight = 1000;
         setUpDisplaySizeWithApp(screenWidth, screenHeight);
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
-                .build();
+        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
 
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate real display with top insets.
@@ -2832,12 +2405,7 @@
         final int screenWidth = 1000;
         final int screenHeight = 1800;
         setUpDisplaySizeWithApp(screenWidth, screenHeight);
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
-                .build();
+        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
 
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         // Simulate real display with top insets.
@@ -2875,12 +2443,7 @@
         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
-                .build();
+        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
 
         // Non-resizable portrait activity
         prepareUnresizable(activity, 0f, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
@@ -2910,12 +2473,7 @@
         mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.33f);
 
         // Create a size compat activity on the same task.
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
-                .build();
+        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
 
         final TestSplitOrganizer organizer =
                 new TestSplitOrganizer(mAtm, activity.getDisplayContent());
@@ -3973,17 +3531,13 @@
 
     @Test
     public void testLetterboxDetailsForStatusBar_letterboxNotOverlappingStatusBar() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         // Align to center so that we don't overlap with the status bar
         mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
                 .setNotch(100)
                 .build();
         setUpApp(display);
+        mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
         TestWindowState statusBar = addStatusBar(mActivity.mDisplayContent);
         spyOn(statusBar);
         doReturn(new Rect(0, 0, statusBar.mRequestedWidth, statusBar.mRequestedHeight))
@@ -3995,12 +3549,12 @@
         // Refresh the letterbox
         mActivity.mRootWindowContainer.performSurfacePlacement();
 
-        Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
-        assertEquals(mBounds, new Rect(0, 900, 1000, 2000));
+        Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+        assertEquals(new Rect(0, 900, 1000, 2000), bounds);
 
         DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
         LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails(
-                mBounds,
+                bounds,
                 mActivity.getDisplayContent().getBounds(),
                 mActivity.findMainWindow().mAttrs.insetsFlags.appearance
         )};
@@ -4289,12 +3843,8 @@
         assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
         display.sendNewConfiguration();
 
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         // Activity should not be letterboxed and should have portrait app bounds even though
@@ -4326,12 +3876,8 @@
         assertTrue(display.getDisplayPolicy().updateDecorInsetsInfo());
         display.sendNewConfiguration();
 
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
                 .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
                 .build();
 
         final Rect bounds = activity.getBounds();
@@ -4358,14 +3904,11 @@
         assertTrue(dc.getDisplayPolicy().updateDecorInsetsInfo());
         dc.sendNewConfiguration();
 
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setComponent(ComponentName.createRelative(mContext,
-                        SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+                .setMinAspectRatio(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE)
                 .build();
-        prepareMinAspectRatio(activity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
-                SCREEN_ORIENTATION_LANDSCAPE);
         // To force config to update again but with the same landscape orientation.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
 
@@ -4379,11 +3922,6 @@
 
     @Test
     public void testApplyAspectRatio_activityAlignWithParentAppVertical() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         // The display's app bounds will be (0, 100, 1000, 2350)
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500)
                 .setCanRotate(false)
@@ -4391,19 +3929,16 @@
                 .build();
 
         setUpApp(display);
-        prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+        mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
+        prepareUnresizable(mActivity, 2.1f, SCREEN_ORIENTATION_UNSPECIFIED);
         // The activity height is 2100 and the display's app bounds height is 2250, so the activity
         // can be aligned inside parentAppBounds
-        assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 2200));
+        assertEquals(new Rect(0, 0, 1000, 2200), mActivity.getBounds());
     }
 
     @Test
+    @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testApplyAspectRatio_activityCannotAlignWithParentAppVertical() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         // The display's app bounds will be (0, 100, 1000, 2150)
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2300)
                 .setCanRotate(false)
@@ -4411,19 +3946,21 @@
                 .build();
 
         setUpApp(display);
-        prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setMaxAspectRatio(2.1f)
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
+                .build();
+
         // The activity height is 2100 and the display's app bounds height is 2050, so the activity
         // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
-        assertEquals(mActivity.getBounds(), display.getBounds());
+        assertEquals(activity.getBounds(), display.getBounds());
     }
 
     @Test
+    @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testApplyAspectRatio_activityAlignWithParentAppHorizontal() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         // The display's app bounds will be (100, 0, 2350, 1000)
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2500, 1000)
                 .setCanRotate(false)
@@ -4431,18 +3968,19 @@
                 .build();
 
         setUpApp(display);
-        prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setMaxAspectRatio(2.1f)
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
+                .build();
         // The activity width is 2100 and the display's app bounds width is 2250, so the activity
         // can be aligned inside parentAppBounds
-        assertEquals(mActivity.getBounds(), new Rect(175, 0, 2275, 1000));
+        assertEquals(activity.getBounds(), new Rect(175, 0, 2275, 1000));
     }
     @Test
+    @DisableCompatChanges({ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED})
     public void testApplyAspectRatio_activityCannotAlignWithParentAppHorizontal() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         // The display's app bounds will be (100, 0, 2150, 1000)
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2300, 1000)
                 .setCanRotate(false)
@@ -4450,10 +3988,14 @@
                 .build();
 
         setUpApp(display);
-        prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+        final ActivityRecord activity = getActivityBuilderOnSameTask()
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setMaxAspectRatio(2.1f)
+                .setScreenOrientation(SCREEN_ORIENTATION_UNSPECIFIED)
+                .build();
         // The activity width is 2100 and the display's app bounds width is 2050, so the activity
         // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
-        assertEquals(mActivity.getBounds(), display.getBounds());
+        assertEquals(activity.getBounds(), display.getBounds());
     }
 
     @Test
@@ -4579,6 +4121,35 @@
     }
 
     @Test
+    public void testUpdateResolvedBoundsVerticalPosition_unfoldDisplay_notTabletop() {
+        // Set up a display in portrait with a fixed-orientation LANDSCAPE app.
+        setUpDisplaySizeWithApp(1000, 2000);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(
+                1.0f /*letterboxVerticalPositionMultiplier*/);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+
+        // Make the activity full-screen.
+        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        // Simulate display unfolding.
+        setFoldablePosture(true /* isHalfFolded */, true /* isTabletop */);
+        doReturn(true).when(mActivity.mDisplayContent).inTransition();
+        resizeDisplay(mTask.mDisplayContent, 1400, 2800);
+
+        // Make sure app doesn't jump to top (default tabletop position) when unfolding.
+        assertEquals(1.0f, mActivity.mLetterboxUiController.getVerticalPositionMultiplier(
+                mActivity.getParent().getConfiguration()), 0);
+
+        // Simulate display fully open after unfolding.
+        setFoldablePosture(false /* isHalfFolded */, false /* isTabletop */);
+        doReturn(false).when(mActivity.mDisplayContent).inTransition();
+
+        assertEquals(1.0f, mActivity.mLetterboxUiController.getVerticalPositionMultiplier(
+                mActivity.getParent().getConfiguration()), 0);
+    }
+
+    @Test
     public void testGetFixedOrientationLetterboxAspectRatio_tabletop_centered() {
         // Set up a display in portrait with a fixed-orientation LANDSCAPE app
         setUpDisplaySizeWithApp(1400, 2800);
@@ -4681,26 +4252,23 @@
 
     @Test
     public void testUpdateResolvedBoundsPosition_alignToTop() {
-        if (Flags.insetsDecoupledConfiguration()) {
-            // TODO (b/151861875): Re-enable it. This is disabled temporarily because the config
-            //  bounds no longer contains display cutout.
-            return;
-        }
         final int notchHeight = 100;
         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
                 .setNotch(notchHeight)
                 .build();
         setUpApp(display);
 
+        mActivity.mResolveConfigHint.mUseOverrideInsetsForConfig = true;
         // Prepare unresizable activity with max aspect ratio
-        prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
+        prepareUnresizable(mActivity, 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
 
-        Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+        Rect bounds = new Rect(mActivity.getWindowConfiguration().getBounds());
         Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
         // The insets should be cut for aspect ratio and then added back because the appBounds
         // are aligned to the top of the parentAppBounds
-        assertEquals(mBounds, new Rect(0, 0, 1000, 1200));
-        assertEquals(appBounds, new Rect(0, notchHeight, 1000, 1200));
+        assertEquals(new Rect(0, notchHeight, 1000, 1200), appBounds);
+        assertEquals(new Rect(0, 0, 1000, 1200), bounds);
+
     }
 
     private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -5196,15 +4764,19 @@
      */
     private ActivityRecord buildActivityRecord(boolean supportsSizeChanges, int resizeMode,
             @ScreenOrientation int screenOrientation) {
-        return new ActivityBuilder(mAtm)
-                .setTask(mTask)
+        return getActivityBuilderOnSameTask()
                 .setResizeMode(resizeMode)
                 .setSupportsSizeChanges(supportsSizeChanges)
                 .setScreenOrientation(screenOrientation)
+                .build();
+    }
+
+    private ActivityBuilder getActivityBuilderOnSameTask() {
+        return new ActivityBuilder(mAtm)
+                .setTask(mTask)
                 .setComponent(ComponentName.createRelative(mContext,
                         SizeCompatTests.class.getName()))
-                .setUid(android.os.Process.myUid())
-                .build();
+                .setUid(android.os.Process.myUid());
     }
 
     static void prepareMinAspectRatio(ActivityRecord activity, float minAspect,
@@ -5268,21 +4840,29 @@
         }
     }
 
-    /** Asserts that the size of activity is larger than its parent so it is scaling. */
     private void assertScaled() {
-        assertTrue(mActivity.inSizeCompatMode());
-        assertNotEquals(1f, mActivity.getCompatScale(), 0.0001f /* delta */);
+        assertScaled(mActivity);
+    }
+
+    /** Asserts that the size of activity is larger than its parent so it is scaling. */
+    private void assertScaled(ActivityRecord activity) {
+        assertTrue(activity.inSizeCompatMode());
+        assertNotEquals(1f, activity.getCompatScale(), 0.0001f /* delta */);
+    }
+
+    private void assertFitted() {
+        assertFitted(mActivity);
     }
 
     /** Asserts that the activity is best fitted in the parent. */
-    private void assertFitted() {
-        final boolean inSizeCompatMode = mActivity.inSizeCompatMode();
+    private void assertFitted(ActivityRecord activity) {
+        final boolean inSizeCompatMode = activity.inSizeCompatMode();
         final String failedConfigInfo = inSizeCompatMode
-                ? ("ParentConfig=" + mActivity.getParent().getConfiguration()
-                        + " ActivityConfig=" + mActivity.getConfiguration())
+                ? ("ParentConfig=" + activity.getParent().getConfiguration()
+                        + " ActivityConfig=" + activity.getConfiguration())
                 : "";
         assertFalse(failedConfigInfo, inSizeCompatMode);
-        assertFalse(mActivity.hasSizeCompatBounds());
+        assertFalse(activity.hasSizeCompatBounds());
     }
 
     /** Asserts the activity max bounds inherit from the TaskDisplayArea. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index c45c86c..c962a3f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -445,6 +445,9 @@
                 if (dc.mDisplayRotationCompatPolicy != null) {
                     dc.mDisplayRotationCompatPolicy.dispose();
                 }
+                if (dc.mCameraCompatFreeformPolicy != null) {
+                    dc.mCameraCompatFreeformPolicy.dispose();
+                }
                 if (dc.mCameraStateMonitor != null) {
                     dc.mCameraStateMonitor.dispose();
                 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index d9fd312..9670a9a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -1902,7 +1902,7 @@
 
         assertApplyTransactionAllowed(mTransaction);
 
-        verify(task).moveOrCreateDecorSurfaceFor(tf);
+        verify(task).moveOrCreateDecorSurfaceFor(tf, true /* visible */);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index d57a7e6..f94e5e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -56,6 +56,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 
+import android.app.ActivityOptions;
 import android.content.pm.SigningDetails;
 import android.content.res.Configuration;
 import android.graphics.Color;
@@ -291,6 +292,30 @@
     }
 
     @Test
+    public void testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities() {
+        final ActivityOptions opts = ActivityOptions.makeBasic();
+        opts.setTaskAlwaysOnTop(true);
+        opts.setLaunchedFromBubble(true);
+        ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build();
+        mTaskFragment.addChild(activity);
+
+        assertNull(mTaskFragment.getTopNonFinishingActivity(true, false));
+    }
+
+    @Test
+    public void testFindTopNonFinishingActivity_includesLaunchedFromBubbleActivities() {
+        final ActivityOptions opts = ActivityOptions.makeBasic();
+        opts.setTaskAlwaysOnTop(true);
+        opts.setLaunchedFromBubble(true);
+        ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build();
+        mTaskFragment.addChild(activity);
+
+        assertEquals(mTaskFragment.getTopNonFinishingActivity(true, true), activity);
+    }
+
+    @Test
     public void testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment() {
         final Task bottomTask = createTask(mDisplayContent);
         final ActivityRecord bottomActivity = createActivityRecord(bottomTask);
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 225e85e..e01cea3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -69,6 +69,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.CameraCompatTaskInfo;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -1695,7 +1696,7 @@
 
         // Decor surface should be created.
         clearInvocations(task);
-        task.moveOrCreateDecorSurfaceFor(fragment);
+        task.moveOrCreateDecorSurfaceFor(fragment, true /* visible */);
 
         assertNotNull(task.mDecorSurfaceContainer);
         assertNotNull(task.getDecorSurface());
@@ -1722,14 +1723,14 @@
         final TaskFragment fragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
         doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded();
 
-        task.moveOrCreateDecorSurfaceFor(fragment1);
+        task.moveOrCreateDecorSurfaceFor(fragment1, true /* visible */);
 
         assertNotNull(task.mDecorSurfaceContainer);
         assertNotNull(task.getDecorSurface());
         assertEquals(fragment1, task.mDecorSurfaceContainer.mOwnerTaskFragment);
 
         // Transfer ownership
-        task.moveOrCreateDecorSurfaceFor(fragment2);
+        task.moveOrCreateDecorSurfaceFor(fragment2, true /* visible */);
 
         assertNotNull(task.mDecorSurfaceContainer);
         assertNotNull(task.getDecorSurface());
@@ -1775,7 +1776,7 @@
         doReturn(true).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
         doReturn(true).when(fragment1).isVisible();
 
-        task.moveOrCreateDecorSurfaceFor(fragment1);
+        task.moveOrCreateDecorSurfaceFor(fragment1, true /* visible */);
         task.assignChildLayers(t);
 
         verify(unembeddedActivity).assignLayer(t, 0);
@@ -1880,7 +1881,7 @@
         doReturn(false).when(fragment2).isAllowedToBeEmbeddedInTrustedMode();
         doReturn(true).when(fragment1).isVisible();
 
-        task.moveOrCreateDecorSurfaceFor(fragment1);
+        task.moveOrCreateDecorSurfaceFor(fragment1, true /* visible */);
 
         clearInvocations(t);
         clearInvocations(unembeddedActivity);
@@ -1889,7 +1890,8 @@
 
         // The decor surface should be placed above all the windows when boosted and the cover
         // surface should show.
-        task.setDecorSurfaceBoosted(fragment1, true /* isBoosted */, clientTransaction);
+        task.requestDecorSurfaceBoosted(fragment1, true /* isBoosted */, clientTransaction);
+        task.commitDecorSurfaceBoostedState();
 
         verify(unembeddedActivity).assignLayer(t, 0);
         verify(fragment1).assignLayer(t, 1);
@@ -1906,8 +1908,9 @@
 
         // The decor surface should be placed just above the owner TaskFragment and the cover
         // surface should hide.
-        task.moveOrCreateDecorSurfaceFor(fragment1);
-        task.setDecorSurfaceBoosted(fragment1, false /* isBoosted */, clientTransaction);
+        task.moveOrCreateDecorSurfaceFor(fragment1, true /* visible */);
+        task.requestDecorSurfaceBoosted(fragment1, false /* isBoosted */, clientTransaction);
+        task.commitDecorSurfaceBoostedState();
 
         verify(unembeddedActivity).assignLayer(t, 0);
         verify(fragment1).assignLayer(t, 1);
@@ -1984,6 +1987,17 @@
         assertNotEquals(activityDifferentPackage, task.getBottomMostActivityInSamePackage());
     }
 
+    @Test
+    public void getTaskInfoPropagatesCameraCompatMode() {
+        final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
+        final ActivityRecord activity = task.getTopMostActivity();
+        activity.mLetterboxUiController
+                .setFreeformCameraCompatMode(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT);
+
+        assertEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT,
+                task.getTaskInfo().appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode);
+    }
+
     private Task getTestTask() {
         return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
new file mode 100644
index 0000000..1d6e307
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -0,0 +1,637 @@
+/*
+ * 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.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+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_UNSPECIFIED;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.WindowConfiguration;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Test class for {@link TransparentPolicy}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:TransparentPolicyTest
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class TransparentPolicyTest extends WindowTestsBase {
+
+    @Test
+    public void testNotStartingWhenDisabled() {
+        runTestScenario((robot) -> {
+            robot.launchTransparentActivityInTask();
+
+            robot.checkTopActivityPolicyStateIsNotRunning();
+        }, /* policyEnabled */ false);
+    }
+
+    @Test
+    public void testNotStartingWithoutTask() {
+        runTestScenario((robot) -> {
+            robot.launchTransparentActivity();
+
+            robot.checkTopActivityPolicyStartNotInvoked();
+            robot.checkTopActivityPolicyStateIsNotRunning();
+        });
+    }
+
+    @Test
+    public void testPolicyRunningWhenTransparentIsUsed() {
+        runTestScenario((robot) -> {
+            robot.launchTransparentActivityInTask();
+
+            robot.checkTopActivityPolicyStartNotInvoked();
+            robot.checkTopActivityPolicyStateIsRunning();
+        });
+    }
+
+    @Test
+    public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
+        runTestScenario((robot) -> {
+            robot.launchTransparentActivityInTask();
+            robot.checkTopActivityPolicyStartNotInvoked();
+            robot.checkTopActivityPolicyStateIsRunning();
+
+            robot.clearInteractions();
+            robot.destroyTopActivity();
+
+            robot.checkTopActivityPolicyStopInvoked();
+            robot.checkTopActivityPolicyStateIsNotRunning();
+        });
+    }
+
+    @Test
+    public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
+        runTestScenario((robot) -> {
+            robot.launchOpaqueActivityInTask();
+            robot.checkTopActivityPolicyStateIsNotRunning();
+
+            robot.launchTransparentActivityInTask();
+            robot.checkTopActivityPolicyStateIsRunning();
+
+            robot.destroyActivity(/* fromTop */ 1);
+            robot.checkTopActivityPolicyStartInvoked();
+        });
+    }
+
+    @Test
+    public void testResetOpaqueReferenceWhenOpaqueIsDestroyed() {
+        runTestScenario((robot) -> {
+            robot.launchTransparentActivityInTask();
+
+            robot.clearInteractions();
+            robot.destroyActivity(/* fromTop */ 1);
+
+            robot.checkTopActivityPolicyStartInvoked();
+            robot.checkTopActivityPolicyStateIsNotRunning();
+        });
+    }
+
+    @Test
+    public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
+        runTestScenario((robot) -> {
+            robot.launchOpaqueActivityInTask();
+            robot.checkTopActivityPolicyStateIsNotRunning();
+
+            robot.launchTransparentActivityInTask();
+            robot.checkTopActivityPolicyStateIsRunning();
+
+            robot.clearInteractions();
+            robot.checkTopActivityPolicyStopNotInvoked();
+        });
+    }
+
+    @Test
+    public void testApplyStrategyToTranslucentActivities() {
+        runTestScenario((robot) -> {
+            robot.configureTopActivity(/* minAspect */ 1.2f, /* maxAspect */ 1.5f,
+                    SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ true);
+            robot.configureTopActivityIgnoreOrientationRequest(true);
+            robot.launchActivity(/* minAspect */ 1.1f, /* maxAspect */ 3f,
+                    SCREEN_ORIENTATION_LANDSCAPE, /* transparent */true, /* addToTask */true);
+            robot.checkTopActivityPolicyStateIsRunning();
+            robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+            robot.checkTopOrientation(SCREEN_ORIENTATION_PORTRAIT);
+            robot.checkTopAspectRatios(/* minAspectRatio */ 1.2f, /* maxAspectRatio */ 1.5f);
+        });
+    }
+
+    @Test
+    public void testApplyStrategyToTransparentActivitiesRetainsWindowConfigurationProperties() {
+        runTestScenario((robot) -> {
+            robot.launchTransparentActivity();
+
+            robot.forceChangeInTopActivityConfiguration();
+            robot.attachTopActivityToTask();
+
+            robot.checkTopActivityConfigurationConfiguration();
+        });
+    }
+
+    @Test
+    public void testApplyStrategyToMultipleTranslucentActivities() {
+        runTestScenario((robot) -> {
+            robot.launchTransparentActivityInTask();
+            robot.checkTopActivityPolicyStateIsRunning();
+            robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+
+            robot.launchTransparentActivityInTask();
+            robot.checkTopActivityPolicyStateIsRunning();
+            robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 2);
+        });
+    }
+
+    @Test
+    public void testNotApplyStrategyToTranslucentActivitiesOverEmbeddedActivities() {
+        runTestScenario((robot) -> {
+            robot.configureTopActivityAsEmbedded();
+            robot.launchTransparentActivityInTask();
+
+            robot.checkTopActivityPolicyStartNotInvoked();
+            robot.checkTopActivityPolicyStateIsNotRunning();
+        });
+    }
+
+    @Test
+    public void testTranslucentActivitiesDontGoInSizeCompatMode() {
+        runTestScenario((robot) -> {
+            robot.configureTopActivityIgnoreOrientationRequest(true);
+            robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.rotateDisplayForTopActivity(ROTATION_90);
+            robot.checkTopActivitySizeCompatMode(/* inScm */ true);
+            robot.rotateDisplayForTopActivity(ROTATION_0);
+            robot.checkTopActivitySizeCompatMode(/* inScm */ false);
+
+            robot.launchTransparentActivityInTask();
+            robot.checkTopActivitySizeCompatMode(/* inScm */ false);
+            robot.rotateDisplayForTopActivity(ROTATION_90);
+            robot.checkTopActivitySizeCompatMode(/* inScm */ false);
+        }, /* displayWidth */ 2800,  /* displayHeight */ 1400);
+    }
+
+    @Test
+    public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() {
+        runTestScenario((robot) -> {
+            robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.configureTopActivityIgnoreOrientationRequest(true);
+            robot.launchTransparentActivity();
+
+            robot.assertFalseOnTopActivity(ActivityRecord::fillsParent);
+            robot.assertTrueOnActivity(/* fromTop */ 1, ActivityRecord::fillsParent);
+            robot.applyTo(/* fromTop */ 1, (activity) -> {
+                activity.finishing = true;
+            });
+            robot.assertFalseOnActivity(/* fromTop */ 1, ActivityRecord::occludesParent);
+            robot.attachTopActivityToTask();
+
+            robot.checkTopActivityPolicyStateIsNotRunning();
+        });
+    }
+
+    @Test
+    public void testTranslucentActivitiesWhenUnfolding() {
+        runTestScenario((robot) -> {
+            robot.applyToTop((activity) -> {
+                activity.mWmService.mLetterboxConfiguration
+                        .setLetterboxHorizontalPositionMultiplier(1.0f);
+            });
+            robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            robot.configureTopActivityIgnoreOrientationRequest(true);
+            robot.launchTransparentActivityInTask();
+            robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+
+            robot.configureTaskWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+            robot.configureTopActivityFoldablePosture(/* isHalfFolded */ true,
+                    /* isTabletop */ false);
+            robot.checkTopActivityRecomputedConfiguration();
+            robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+            robot.clearInteractions();
+
+            robot.configureTopActivityFoldablePosture(/* isHalfFolded */ false,
+                    /* isTabletop */ false);
+            robot.checkTopActivityRecomputedConfiguration();
+            robot.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
+        }, /* displayWidth */ 2800,  /* displayHeight */ 1400);
+    }
+
+
+    @Test
+    public void testTranslucentActivity_clearSizeCompatMode_inheritedCompatDisplayInsetsCleared() {
+        runTestScenario((robot) -> {
+            robot.configureTopActivityIgnoreOrientationRequest(true);
+            robot.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            // Rotate to put activity in size compat mode.
+            robot.rotateDisplayForTopActivity(ROTATION_90);
+            robot.checkTopActivitySizeCompatMode(/* inScm */ true);
+
+            robot.launchTransparentActivityInTask();
+            robot.assertNotNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+            robot.applyToTop(ActivityRecord::clearSizeCompatMode);
+            robot.assertNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+        });
+    }
+
+    private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer,
+                                 boolean policyEnabled, int displayWidth, int displayHeight) {
+        spyOn(mWm.mLetterboxConfiguration);
+        when(mWm.mLetterboxConfiguration.isTranslucentLetterboxingEnabled())
+                .thenReturn(policyEnabled);
+        final TestDisplayContent.Builder builder = new TestDisplayContent.Builder(mAtm,
+                displayWidth, displayHeight);
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(builder.build())
+                .setCreateActivity(true).build();
+        final ActivityRecord opaqueActivity = task.getTopNonFinishingActivity();
+        final TransparentPolicyRobotTest robot = new TransparentPolicyRobotTest(mAtm, task,
+                opaqueActivity);
+        consumer.accept(robot);
+    }
+
+    private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer,
+                                 int displayWidth, int displayHeight) {
+        runTestScenario(consumer, /* policyEnabled */ true, displayWidth, displayHeight);
+    }
+
+    private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer,
+                                 boolean policyEnabled) {
+        runTestScenario(consumer, policyEnabled, /* displayWidth */ 2000,
+                /* displayHeight */ 1000);
+    }
+
+    private void runTestScenario(Consumer<TransparentPolicyRobotTest> consumer) {
+        runTestScenario(consumer, /* policyEnabled */ true);
+    }
+
+    /**
+     * Robot pattern implementation for TransparentPolicy
+     * TODO(b/344587983): Extract Robot to be reused in different test classes.
+     */
+    private static class TransparentPolicyRobotTest {
+
+        private final ActivityTaskManagerService mAtm;
+
+        private final Task mTask;
+
+        private final ActivityStackTest mActivityStack;
+
+        private WindowConfiguration mTopActivityWindowConfiguration;
+
+        private TransparentPolicyRobotTest(ActivityTaskManagerService atm, Task task,
+                                           ActivityRecord opaqueActivity) {
+            mAtm = atm;
+            mTask = task;
+            mActivityStack = new ActivityStackTest();
+            mActivityStack.pushActivity(opaqueActivity);
+            spyOn(opaqueActivity.mTransparentPolicy);
+        }
+
+        void configureTopActivityAsEmbedded() {
+            final ActivityRecord topActivity = mActivityStack.top();
+            spyOn(topActivity);
+            doReturn(true).when(topActivity).isEmbedded();
+        }
+
+        private void launchActivity(float minAspectRatio, float maxAspectRatio,
+                                    @Configuration.Orientation int orientation, boolean transparent,
+                                    boolean addToTask) {
+            final ActivityBuilder activityBuilder = new ActivityBuilder(mAtm)
+                    .setScreenOrientation(orientation)
+                    .setLaunchedFromUid(mActivityStack.base().getUid());
+            if (transparent) {
+                activityBuilder.setActivityTheme(android.R.style.Theme_Translucent);
+            }
+            if (minAspectRatio >= 0) {
+                activityBuilder.setMinAspectRatio(minAspectRatio);
+            }
+            if (maxAspectRatio >= 0) {
+                activityBuilder.setMaxAspectRatio(maxAspectRatio);
+            }
+            final ActivityRecord newActivity = activityBuilder.build();
+            if (addToTask) {
+                mTask.addChild(newActivity);
+            }
+            spyOn(newActivity.mTransparentPolicy);
+            mActivityStack.pushActivity(newActivity);
+        }
+
+        void attachTopActivityToTask() {
+            mTask.addChild(mActivityStack.top());
+        }
+
+        void launchTransparentActivity() {
+            launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
+                    SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
+                    /* addToTask */ false);
+        }
+
+        void launchTransparentActivityInTask() {
+            launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
+                    SCREEN_ORIENTATION_PORTRAIT, /* transparent */ true,
+                    /* addToTask */true);
+        }
+
+        void launchOpaqueActivityInTask() {
+            launchActivity(/*minAspectRatio */ -1, /* maxAspectRatio */ -1,
+                    SCREEN_ORIENTATION_PORTRAIT, /* transparent */ false,
+                    /* addToTask */true);
+        }
+
+        void destroyTopActivity() {
+            mActivityStack.top().removeImmediately();
+        }
+
+        void destroyActivity(int fromTop) {
+            mActivityStack.applyTo(/* fromTop */ fromTop, ActivityRecord::removeImmediately);
+        }
+
+        void forceChangeInTopActivityConfiguration() {
+            mActivityStack.applyToTop((activity) -> {
+                final Configuration requestedConfig = activity.getRequestedOverrideConfiguration();
+                mTopActivityWindowConfiguration = requestedConfig.windowConfiguration;
+                mTopActivityWindowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+                mTopActivityWindowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+                mTopActivityWindowConfiguration.setAlwaysOnTop(true);
+                activity.onRequestedOverrideConfigurationChanged(requestedConfig);
+            });
+        }
+
+        void checkTopActivityPolicyStateIsRunning() {
+            assertTrue(mActivityStack.top().mTransparentPolicy.isRunning());
+        }
+
+        void checkTopActivityPolicyStateIsNotRunning() {
+            assertFalse(mActivityStack.top().mTransparentPolicy.isRunning());
+        }
+
+        void checkTopActivityPolicyStopInvoked() {
+            verify(mActivityStack.top().mTransparentPolicy).stop();
+        }
+
+        void checkTopActivityPolicyStopNotInvoked() {
+            mActivityStack.applyToTop((activity) -> {
+                verify(activity.mTransparentPolicy, never()).stop();
+            });
+        }
+
+        void checkTopActivityPolicyStartInvoked() {
+            mActivityStack.applyToTop((activity) -> {
+                verify(activity.mTransparentPolicy).start();
+            });
+        }
+
+        void checkTopActivityPolicyStartNotInvoked() {
+            verify(mActivityStack.top().mTransparentPolicy, never()).start();
+        }
+
+        void assertTrueOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
+            mActivityStack.applyTo(fromTop, (activity) -> {
+                Assert.assertTrue(predicate.test(activity));
+            });
+        }
+
+        void assertFalseOnTopActivity(Predicate<ActivityRecord> predicate) {
+            Assert.assertFalse(predicate.test(mActivityStack.top()));
+        }
+
+        void assertFalseOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
+            mActivityStack.applyTo(fromTop, (activity) -> {
+                Assert.assertFalse(predicate.test(activity));
+            });
+        }
+
+        void assertNotNullOnTopActivity(Function<ActivityRecord, Object> getter) {
+            Assert.assertNotNull(getter.apply(mActivityStack.top()));
+        }
+
+        void assertNullOnTopActivity(Function<ActivityRecord, Object> getter) {
+            Assert.assertNull(getter.apply(mActivityStack.top()));
+        }
+
+        void checkTopActivityConfigurationConfiguration() {
+            mActivityStack.applyToTop((activity) -> {
+                // The original override of WindowConfiguration should keep.
+                assertEquals(ACTIVITY_TYPE_STANDARD, activity.getActivityType());
+                assertEquals(WINDOWING_MODE_MULTI_WINDOW,
+                        mTopActivityWindowConfiguration.getWindowingMode());
+                assertTrue(mTopActivityWindowConfiguration.isAlwaysOnTop());
+                // Unless display is going to be rotated, it should always inherit from parent.
+                assertEquals(ROTATION_UNDEFINED,
+                        mTopActivityWindowConfiguration.getDisplayRotation());
+            });
+        }
+
+        void checkTopActivityHasInheritedBoundsFrom(int fromTop) {
+            final ActivityRecord topActivity = mActivityStack.top();
+            final ActivityRecord otherActivity = mActivityStack.getFromTop(/* fromTop */ fromTop);
+            final Rect opaqueBounds = otherActivity.getConfiguration().windowConfiguration
+                    .getBounds();
+            final Rect translucentRequestedBounds = topActivity.getRequestedOverrideBounds();
+            Assert.assertEquals(opaqueBounds, translucentRequestedBounds);
+        }
+
+        void checkTopActivityRecomputedConfiguration() {
+            verify(mActivityStack.top()).recomputeConfiguration();
+        }
+
+        void checkTopOrientation(int orientation) {
+            Assert.assertEquals(orientation, mActivityStack.top()
+                    .getRequestedConfigurationOrientation());
+        }
+
+        void configureTaskWindowingMode(int windowingMode) {
+            mTask.setWindowingMode(windowingMode);
+        }
+
+        void checkTopAspectRatios(float minAspectRatio, float maxAspectRatio) {
+            final ActivityRecord topActivity = mActivityStack.top();
+            Assert.assertEquals(minAspectRatio, topActivity.getMinAspectRatio(), 0.0001);
+            Assert.assertEquals(maxAspectRatio, topActivity.getMaxAspectRatio(), 0.0001);
+        }
+
+        void checkTopActivitySizeCompatMode(boolean inScm) {
+            Assert.assertEquals(inScm, mActivityStack.top().inSizeCompatMode());
+        }
+
+        void clearInteractions() {
+            mActivityStack.applyToAll((activity) -> {
+                clearInvocations(activity);
+                clearInvocations(activity.mTransparentPolicy);
+            });
+        }
+
+        void configureTopActivity(float minAspect, float maxAspect, int screenOrientation,
+                                  boolean isUnresizable) {
+            prepareLimitedBounds(mActivityStack.top(), minAspect, maxAspect, screenOrientation,
+                    isUnresizable);
+        }
+
+        void configureTopActivityIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+            mActivityStack.top().mDisplayContent
+                    .setIgnoreOrientationRequest(ignoreOrientationRequest);
+        }
+
+        void configureUnresizableTopActivity(int screenOrientation) {
+            configureTopActivity(-1, -1, screenOrientation, true);
+        }
+
+        void applyToTop(Consumer<ActivityRecord> consumer) {
+            consumer.accept(mActivityStack.top());
+        }
+
+        void applyTo(int fromTop, Consumer<ActivityRecord> consumer) {
+            mActivityStack.applyTo(fromTop, consumer);
+        }
+
+        void rotateDisplayForTopActivity(int rotation) {
+            rotateDisplay(mActivityStack.top().mDisplayContent, rotation);
+        }
+
+        /**
+         * Setups activity with restriction on its bounds, such as maxAspect, minAspect,
+         * fixed orientation, and/or whether it is resizable.
+         */
+        void prepareLimitedBounds(ActivityRecord activity, float minAspect, float maxAspect,
+                                  int screenOrientation, boolean isUnresizable) {
+            activity.info.resizeMode = isUnresizable
+                    ? RESIZE_MODE_UNRESIZEABLE
+                    : RESIZE_MODE_RESIZEABLE;
+            final Task task = activity.getTask();
+            if (task != null) {
+                // Update the Task resize value as activity will follow the task.
+                task.mResizeMode = activity.info.resizeMode;
+                task.getRootActivity().info.resizeMode = activity.info.resizeMode;
+            }
+            activity.setVisibleRequested(true);
+            if (maxAspect >= 0) {
+                activity.info.setMaxAspectRatio(maxAspect);
+            }
+            if (minAspect >= 0) {
+                activity.info.setMinAspectRatio(minAspect);
+            }
+            if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+                activity.info.screenOrientation = screenOrientation;
+                activity.setRequestedOrientation(screenOrientation);
+            }
+            // Make sure to use the provided configuration to construct the size compat fields.
+            activity.clearSizeCompatMode();
+            activity.ensureActivityConfiguration();
+            // Make sure the display configuration reflects the change of activity.
+            if (activity.mDisplayContent.updateOrientation()) {
+                activity.mDisplayContent.sendNewConfiguration();
+            }
+        }
+
+        void configureTopActivityFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
+            mActivityStack.applyToTop((activity) -> {
+                final DisplayRotation r = activity.mDisplayContent.getDisplayRotation();
+                doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
+                doReturn(false).when(r)
+                        .isDeviceInPosture(any(DeviceStateController.DeviceState.class),
+                                anyBoolean());
+                if (isHalfFolded) {
+                    doReturn(true).when(r)
+                            .isDeviceInPosture(DeviceStateController.DeviceState.HALF_FOLDED,
+                                    isTabletop);
+                }
+                activity.recomputeConfiguration();
+            });
+        }
+
+        private static void rotateDisplay(DisplayContent display, int rotation) {
+            final Configuration c = new Configuration();
+            display.getDisplayRotation().setRotation(rotation);
+            display.computeScreenConfiguration(c);
+            display.onRequestedOverrideConfigurationChanged(c);
+        }
+
+        /**
+         * Contains all the ActivityRecord launched in the test. This is different from what's in
+         * the Task because activities are added here even if not added to tasks.
+         */
+        private static class ActivityStackTest {
+            private final List<ActivityRecord> mActivities = new ArrayList<>();
+
+            void pushActivity(ActivityRecord activityRecord) {
+                mActivities.add(activityRecord);
+            }
+
+            void applyToTop(Consumer<ActivityRecord> consumer) {
+                consumer.accept(top());
+            }
+
+            ActivityRecord getFromTop(int fromTop) {
+                return mActivities.get(mActivities.size() - fromTop - 1);
+            }
+
+            ActivityRecord base() {
+                return mActivities.get(0);
+            }
+
+            ActivityRecord top() {
+                return mActivities.get(mActivities.size() - 1);
+            }
+
+            // Allows access to the activity at position beforeLast from the top.
+            // If fromTop = 0 the activity used is the top one.
+            void applyTo(int fromTop, Consumer<ActivityRecord> consumer) {
+                consumer.accept(getFromTop(fromTop));
+            }
+
+            void applyToAll(Consumer<ActivityRecord> consumer) {
+                for (int i = mActivities.size() - 1; i >= 0; i--) {
+                    consumer.accept(mActivities.get(i));
+                }
+            }
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 9f85acb..4ebbf49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -960,10 +960,20 @@
         assertTrue(child.handlesOrientationChangeFromDescendant(orientation));
     }
 
+    private static void addLocalInsets(WindowContainer wc) {
+        final Binder owner = new Binder();
+        Rect genericOverlayInsetsRect1 = new Rect(0, 200, 1080, 700);
+        final InsetsFrameProvider provider1 =
+                new InsetsFrameProvider(owner, 1, WindowInsets.Type.systemOverlays())
+                        .setArbitraryRectangle(genericOverlayInsetsRect1);
+        wc.addLocalInsetsFrameProvider(provider1, owner);
+    }
+
     @Test
     public void testOnDisplayChanged() {
         final Task rootTask = createTask(mDisplayContent);
         final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+        addLocalInsets(task);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
 
         final DisplayContent newDc = createNewDisplay();
@@ -972,6 +982,7 @@
 
         verify(rootTask).onDisplayChanged(newDc);
         verify(task).onDisplayChanged(newDc);
+        assertTrue(task.mLocalInsetsSources.size() == 1);
         verify(activity).onDisplayChanged(newDc);
         assertEquals(newDc, rootTask.mDisplayContent);
         assertEquals(newDc, task.mDisplayContent);
@@ -981,6 +992,7 @@
     @Test
     public void testOnDisplayChanged_cleanupChanging() {
         final Task task = createTask(mDisplayContent);
+        addLocalInsets(task);
         spyOn(task.mSurfaceFreezer);
         mDisplayContent.mChangingContainers.add(task);
 
@@ -988,6 +1000,7 @@
         // This happens on display info changed.
         task.onDisplayChanged(mDisplayContent);
 
+        assertTrue(task.mLocalInsetsSources.size() == 1);
         assertTrue(mDisplayContent.mChangingContainers.contains(task));
         verify(task.mSurfaceFreezer, never()).unfreeze(any());
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index fb854c5..ab4deca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -31,6 +31,7 @@
 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -1455,6 +1456,53 @@
     }
 
     @Test
+    public void testReorderWithParents() {
+        /*
+                  root
+               ____|______
+               |         |
+           firstTda    secondTda
+               |             |
+         firstRootTask    secondRootTask
+
+         */
+        final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+                mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+                FEATURE_VENDOR_FIRST);
+        final Task firstRootTask = firstTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+                .setTask(firstRootTask).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+                .setTask(secondRootTask).build();
+        // This assertion is just a defense to ensure that firstRootTask is not the top most
+        // by default
+        assertThat(mDisplayContent.getTopRootTask()).isEqualTo(secondRootTask);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+
+        // Reorder to top
+        wct.reorder(firstRootTask.mRemoteToken.toWindowContainerToken(), true /* onTop */,
+                true /* includingParents */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+        // firstRootTask can only be on the top if its TDA was also reordered to the Top which
+        // in-turn ensures that the reorder happened including the parents.
+        assertThat(mDisplayContent.getTopRootTask()).isEqualTo(firstRootTask);
+
+        // Reorder to bottom
+        wct.reorder(firstRootTask.mRemoteToken.toWindowContainerToken(), false /* onTop */,
+                true /* includingParents */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+
+        // firstRootTask can only be on the bottom if its TDA was also reordered to the bottom
+        // which in-turn ensures that the reorder happened including the parents.
+        assertThat(mDisplayContent.getBottomMostTask()).isEqualTo(firstRootTask);
+    }
+
+    @Test
     public void testAppearDeferThenVanish() {
         final ITaskOrganizer organizer = registerMockOrganizer();
         final Task rootTask = createRootTask();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 2e93cba..27c383c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -403,7 +403,9 @@
 
         mAppStandby.addListener(mStandbyChangeListener);
 
-        mPackageMonitor.register(getContext(), null, UserHandle.ALL, true);
+        mPackageMonitor.register(getContext(),
+                /* thread= */ USE_DEDICATED_HANDLER_THREAD ? mHandler.getLooper() : null,
+                UserHandle.ALL, true);
 
         IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_STARTED);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index e8bac66..175a09d 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -80,6 +80,7 @@
 import android.service.usb.UsbHandlerProto;
 import android.util.Pair;
 import android.util.Slog;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -110,6 +111,8 @@
 import java.util.Scanner;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * UsbDeviceManager manages USB state in device mode.
@@ -135,6 +138,11 @@
      */
     private static final String NORMAL_BOOT = "normal";
 
+    /**
+     *  UDC controller for the ConfigFS USB Gadgets.
+     */
+    private static final String USB_CONTROLLER_NAME_PROPERTY = "sys.usb.controller";
+
     private static final String USB_STATE_MATCH =
             "DEVPATH=/devices/virtual/android_usb/android0";
     private static final String ACCESSORY_START_MATCH =
@@ -932,6 +940,42 @@
             sEventLogger.enqueue(new EventLogger.StringEvent("USB intent: " + intent));
         }
 
+        private void getMidiCardDevice() throws FileNotFoundException {
+            String controllerName =  getSystemProperty(USB_CONTROLLER_NAME_PROPERTY, "");
+            if (TextUtils.isEmpty(controllerName)) {
+                throw new FileNotFoundException("controller name not found");
+            }
+
+            File soundDir = new File("/sys/class/udc/" + controllerName + "/gadget/sound");
+            if (!soundDir.exists()) {
+                throw new FileNotFoundException("sound device not found");
+            }
+
+            // There should be exactly one sound card
+            File[] cardDirs = FileUtils.listFilesOrEmpty(soundDir,
+                                                         (dir, file) -> file.startsWith("card"));
+            if (cardDirs.length != 1) {
+                throw new FileNotFoundException("sound card not match");
+            }
+
+            // There should be exactly one midi device
+            File[] midis = FileUtils.listFilesOrEmpty(cardDirs[0],
+                                                      (dir, file) -> file.startsWith("midi"));
+            if (midis.length != 1) {
+                throw new FileNotFoundException("MIDI device not match");
+            }
+
+            Pattern pattern = Pattern.compile("midiC(\\d+)D(\\d+)");
+            Matcher matcher = pattern.matcher(midis[0].getName());
+            if (matcher.matches()) {
+                mMidiCard = Integer.parseInt(matcher.group(1));
+                mMidiDevice = Integer.parseInt(matcher.group(2));
+                Slog.i(TAG, "Found MIDI card " + mMidiCard + " device " + mMidiDevice);
+            } else {
+                throw new FileNotFoundException("MIDI name not match");
+            }
+        }
+
         private void updateUsbFunctions() {
             updateMidiFunction();
             updateMtpFunction();
@@ -941,17 +985,26 @@
             boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0;
             if (enabled != mMidiEnabled) {
                 if (enabled) {
-                    Scanner scanner = null;
-                    try {
-                        scanner = new Scanner(new File(MIDI_ALSA_PATH));
-                        mMidiCard = scanner.nextInt();
-                        mMidiDevice = scanner.nextInt();
-                    } catch (FileNotFoundException e) {
-                        Slog.e(TAG, "could not open MIDI file", e);
-                        enabled = false;
-                    } finally {
-                        if (scanner != null) {
-                            scanner.close();
+                    if (android.hardware.usb.flags.Flags.enableUsbSysfsMidiIdentification()) {
+                        try {
+                            getMidiCardDevice();
+                        } catch (FileNotFoundException e) {
+                            Slog.e(TAG, "could not identify MIDI device", e);
+                            enabled = false;
+                        }
+                    } else {
+                        Scanner scanner = null;
+                        try {
+                            scanner = new Scanner(new File(MIDI_ALSA_PATH));
+                            mMidiCard = scanner.nextInt();
+                            mMidiDevice = scanner.nextInt();
+                        } catch (FileNotFoundException e) {
+                            Slog.e(TAG, "could not open MIDI file", e);
+                            enabled = false;
+                        } finally {
+                            if (scanner != null) {
+                                scanner.close();
+                            }
                         }
                     }
                 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
old mode 100755
new mode 100644
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
old mode 100755
new mode 100644
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/package.html b/telephony/common/com/google/android/mms/package.html
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/pdu/PduParser.java b/telephony/common/com/google/android/mms/pdu/PduParser.java
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/pdu/package.html b/telephony/common/com/google/android/mms/pdu/package.html
old mode 100755
new mode 100644
diff --git a/telephony/common/com/google/android/mms/util/package.html b/telephony/common/com/google/android/mms/util/package.html
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index bc8f65e..09cb464 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9842,6 +9842,43 @@
     public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL =
             "remove_satellite_plmn_in_manual_network_scan_bool";
 
+
+    /** @hide */
+    @IntDef({
+            SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED,
+            SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED,
+            SATELLITE_DATA_SUPPORT_ALL,
+    })
+    public @interface SATELLITE_DATA_SUPPORT_MODE {}
+
+    /**
+     * Doesn't support unrestricted traffic on satellite network.
+     * @hide
+     */
+    public static final int SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED = 0;
+    /**
+     * Support unrestricted but bandwidth_constrained traffic on satellite network.
+     * @hide
+     */
+    public static final int SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED = 1;
+    /**
+     * Support unrestricted satellite network that serves all traffic.
+     * @hide
+     */
+    public static final int SATELLITE_DATA_SUPPORT_ALL = 2;
+    /**
+     * Indicates what kind of traffic an {@link NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED}
+     * satellite network can possibly support. The network may subject to further
+     * restrictions such as entitlement etc.
+     * If no data is allowed on satellite network, exclude
+     * {@link ApnSetting#INFRASTRUCTURE_SATELLITE} from APN infrastructure_bitmask, and this
+     * configuration is ignored.
+     * By default it only supports restricted data.
+     * @hide
+     */
+    public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT =
+            "satellite_data_support_mode_int";
+
     /**
      * Determine whether to override roaming Wi-Fi Calling preference when device is connected to
      * non-terrestrial network.
@@ -11084,6 +11121,8 @@
         sDefaults.putInt(KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT,
                 CellSignalStrengthLte.USE_RSRP);
         sDefaults.putBoolean(KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL, true);
+        sDefaults.putInt(KEY_SATELLITE_DATA_SUPPORT_MODE_INT,
+                CarrierConfigManager.SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED);
         sDefaults.putBoolean(KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true);
         sDefaults.putInt(KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT, 7);
         sDefaults.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.aidl b/telephony/java/android/telephony/SubscriptionInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
index f5688bf..7356cdc 100644
--- a/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
+++ b/telephony/java/android/telephony/TelephonyFrameworkInitializer.java
@@ -83,7 +83,7 @@
         // Check SDK version of the vendor partition.
         final int vendorApiLevel = SystemProperties.getInt(
                 "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
-        if (vendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) return true;
+        if (vendorApiLevel < Build.VENDOR_API_2024_Q2) return true;
 
         // Check SDK version of the client app.
         if (!Compatibility.isChangeEnabled(ENABLE_CHECKING_TELEPHONY_FEATURES)) return true;
diff --git a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
index c35242d..5f9a524 100644
--- a/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
+++ b/telephony/java/android/telephony/euicc/EuiccRulesAuthTable.java
@@ -242,6 +242,16 @@
                 && Arrays.equals(mPolicyRuleFlags, that.mPolicyRuleFlags);
     }
 
+    @Override
+    public int hashCode() {
+        int result = Arrays.hashCode(mPolicyRules);
+        result = 31 * result + Arrays.hashCode(mPolicyRuleFlags);
+        for (int i = 0; i < mCarrierIds.length; i++) {
+            result = 31 * result + Arrays.hashCode(mCarrierIds[i]);
+        }
+        return result;
+    }
+
     private EuiccRulesAuthTable(Parcel source) {
         mPolicyRules = source.createIntArray();
         int len = mPolicyRules.length;
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
index f548dba..c7af661 100644
--- a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java
@@ -256,6 +256,26 @@
     }
 
     /**
+     * Get the attribute flag ATTR_REGISTRATION_TYPE_EMERGENCY.
+     * @return {@code true} if the ATTR_REGISTRATION_TYPE_EMERGENCY attribute has been set, or
+     * {@code false} if it has not been set.
+     */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+    public boolean getFlagRegistrationTypeEmergency() {
+        return (mImsAttributeFlags & ATTR_REGISTRATION_TYPE_EMERGENCY) != 0;
+    }
+
+    /**
+     * Get the attribute flag ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL.
+     * @return {@code true} if the ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL attribute has been set,
+     * or {@code false} if it has not been set.
+     */
+    @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE)
+    public boolean getFlagVirtualRegistrationForEmergencyCall() {
+        return (mImsAttributeFlags & ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL) != 0;
+    }
+
+    /**
      * Gets the Set of feature tags associated with the current IMS registration, if the IMS
      * service supports supplying this information.
      * <p>
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.aidl b/telephony/java/android/telephony/mbms/DownloadRequest.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/FileInfo.aidl b/telephony/java/android/telephony/mbms/FileInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.aidl b/telephony/java/android/telephony/mbms/FileServiceInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl b/telephony/java/android/telephony/mbms/IGroupCallCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.aidl b/telephony/java/android/telephony/mbms/ServiceInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl b/telephony/java/android/telephony/mbms/StreamingServiceInfo.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.aidl b/telephony/java/android/telephony/mbms/UriPathPair.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 47f53f3..2a359cd 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -193,6 +193,14 @@
 
     /**
      * Bundle key to get the response from
+     * {@link #requestSessionStats(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+
+    public static final String KEY_SESSION_STATS = "session_stats";
+
+    /**
+     * Bundle key to get the response from
      * {@link #requestIsProvisioned(Executor, OutcomeReceiver)}.
      * @hide
      */
@@ -2493,6 +2501,65 @@
         }
     }
 
+    /**
+     * Request to get the {@link SatelliteSessionStats} of the satellite service.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return the {@link SatelliteSessionStats} of the satellite service.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteResult}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @hide
+     */
+    @RequiresPermission(allOf = {Manifest.permission.PACKAGE_USAGE_STATS,
+            Manifest.permission.MODIFY_PHONE_STATE})
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public void requestSessionStats(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<SatelliteSessionStats, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
+                            if (resultData.containsKey(KEY_SESSION_STATS)) {
+                                SatelliteSessionStats stats =
+                                        resultData.getParcelable(KEY_SESSION_STATS,
+                                                SatelliteSessionStats.class);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(stats)));
+                            } else {
+                                loge("KEY_SESSION_STATS does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(new SatelliteException(
+                                                SATELLITE_RESULT_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestSatelliteSessionStats(mSubId, receiver);
+            } else {
+                loge("requestSessionStats() invalid telephony");
+                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+            }
+        } catch (RemoteException ex) {
+            loge("requestSessionStats() RemoteException: " + ex);
+            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
+                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
+        }
+    }
+
     @Nullable
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/telephony/java/android/telephony/satellite/SatelliteSessionStats.aidl
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
copy to telephony/java/android/telephony/satellite/SatelliteSessionStats.aidl
index 23dbc26..4175125 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
+++ b/telephony/java/android/telephony/satellite/SatelliteSessionStats.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * 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
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.systemui.recordissue
+ package android.telephony.satellite;
 
-import com.android.traceur.TraceUtils.PresetTraceType
-
-data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
+ parcelable SatelliteSessionStats;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
new file mode 100644
index 0000000..aabb058
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
@@ -0,0 +1,224 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * SatelliteSessionStats is used to represent the usage stats of the satellite service.
+ * @hide
+ */
+public class SatelliteSessionStats implements Parcelable {
+    private int mCountOfSuccessfulUserMessages;
+    private int mCountOfUnsuccessfulUserMessages;
+    private int mCountOfTimedOutUserMessagesWaitingForConnection;
+    private int mCountOfTimedOutUserMessagesWaitingForAck;
+    private int mCountOfUserMessagesInQueueToBeSent;
+
+    /**
+     * SatelliteSessionStats constructor
+     * @param  builder Builder to create SatelliteSessionStats object/
+     */
+    public SatelliteSessionStats(@NonNull Builder builder) {
+        mCountOfSuccessfulUserMessages = builder.mCountOfSuccessfulUserMessages;
+        mCountOfUnsuccessfulUserMessages = builder.mCountOfUnsuccessfulUserMessages;
+        mCountOfTimedOutUserMessagesWaitingForConnection =
+                builder.mCountOfTimedOutUserMessagesWaitingForConnection;
+        mCountOfTimedOutUserMessagesWaitingForAck =
+                builder.mCountOfTimedOutUserMessagesWaitingForAck;
+        mCountOfUserMessagesInQueueToBeSent = builder.mCountOfUserMessagesInQueueToBeSent;
+    }
+
+    private SatelliteSessionStats(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mCountOfSuccessfulUserMessages);
+        out.writeInt(mCountOfUnsuccessfulUserMessages);
+        out.writeInt(mCountOfTimedOutUserMessagesWaitingForConnection);
+        out.writeInt(mCountOfTimedOutUserMessagesWaitingForAck);
+        out.writeInt(mCountOfUserMessagesInQueueToBeSent);
+    }
+
+    @NonNull
+    public static final Creator<SatelliteSessionStats> CREATOR = new Parcelable.Creator<>() {
+
+        @Override
+        public SatelliteSessionStats createFromParcel(Parcel in) {
+            return new SatelliteSessionStats(in);
+        }
+
+        @Override
+        public SatelliteSessionStats[] newArray(int size) {
+            return new SatelliteSessionStats[size];
+        }
+    };
+
+    @Override
+    @NonNull
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("countOfSuccessfulUserMessages:");
+        sb.append(mCountOfSuccessfulUserMessages);
+        sb.append(",");
+
+        sb.append("countOfUnsuccessfulUserMessages:");
+        sb.append(mCountOfUnsuccessfulUserMessages);
+        sb.append(",");
+
+        sb.append("countOfTimedOutUserMessagesWaitingForConnection:");
+        sb.append(mCountOfTimedOutUserMessagesWaitingForConnection);
+        sb.append(",");
+
+        sb.append("countOfTimedOutUserMessagesWaitingForAck:");
+        sb.append(mCountOfTimedOutUserMessagesWaitingForAck);
+        sb.append(",");
+
+        sb.append("countOfUserMessagesInQueueToBeSent:");
+        sb.append(mCountOfUserMessagesInQueueToBeSent);
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SatelliteSessionStats that = (SatelliteSessionStats) o;
+        return mCountOfSuccessfulUserMessages == that.mCountOfSuccessfulUserMessages
+                && mCountOfUnsuccessfulUserMessages == that.mCountOfUnsuccessfulUserMessages
+                && mCountOfTimedOutUserMessagesWaitingForConnection
+                == that.mCountOfTimedOutUserMessagesWaitingForConnection
+                && mCountOfTimedOutUserMessagesWaitingForAck
+                == that.mCountOfTimedOutUserMessagesWaitingForAck
+                && mCountOfUserMessagesInQueueToBeSent
+                == that.mCountOfUserMessagesInQueueToBeSent;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCountOfSuccessfulUserMessages, mCountOfUnsuccessfulUserMessages,
+                mCountOfTimedOutUserMessagesWaitingForConnection,
+                mCountOfTimedOutUserMessagesWaitingForAck,
+                mCountOfUserMessagesInQueueToBeSent);
+    }
+
+    public int getCountOfSuccessfulUserMessages() {
+        return mCountOfSuccessfulUserMessages;
+    }
+
+    public int getCountOfUnsuccessfulUserMessages() {
+        return mCountOfUnsuccessfulUserMessages;
+    }
+
+    public int getCountOfTimedOutUserMessagesWaitingForConnection() {
+        return mCountOfTimedOutUserMessagesWaitingForConnection;
+    }
+
+    public int getCountOfTimedOutUserMessagesWaitingForAck() {
+        return mCountOfTimedOutUserMessagesWaitingForAck;
+    }
+
+    public int getCountOfUserMessagesInQueueToBeSent() {
+        return mCountOfUserMessagesInQueueToBeSent;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mCountOfSuccessfulUserMessages = in.readInt();
+        mCountOfUnsuccessfulUserMessages = in.readInt();
+        mCountOfTimedOutUserMessagesWaitingForConnection = in.readInt();
+        mCountOfTimedOutUserMessagesWaitingForAck = in.readInt();
+        mCountOfUserMessagesInQueueToBeSent = in.readInt();
+    }
+
+    /**
+     * A builder class to create {@link SatelliteSessionStats} data object.
+     */
+    public static final class Builder {
+        private int mCountOfSuccessfulUserMessages;
+        private int mCountOfUnsuccessfulUserMessages;
+        private int mCountOfTimedOutUserMessagesWaitingForConnection;
+        private int mCountOfTimedOutUserMessagesWaitingForAck;
+        private int mCountOfUserMessagesInQueueToBeSent;
+
+        /**
+         * Sets countOfSuccessfulUserMessages value of {@link SatelliteSessionStats}
+         * and then returns the Builder class.
+         */
+        @NonNull
+        public Builder setCountOfSuccessfulUserMessages(int count) {
+            mCountOfSuccessfulUserMessages = count;
+            return this;
+        }
+
+        /**
+         * Sets countOfUnsuccessfulUserMessages value of {@link SatelliteSessionStats}
+         * and then returns the Builder class.
+         */
+        @NonNull
+        public Builder setCountOfUnsuccessfulUserMessages(int count) {
+            mCountOfUnsuccessfulUserMessages = count;
+            return this;
+        }
+
+        /**
+         * Sets countOfTimedOutUserMessagesWaitingForConnection value of
+         * {@link SatelliteSessionStats} and then returns the Builder class.
+         */
+        @NonNull
+        public Builder setCountOfTimedOutUserMessagesWaitingForConnection(int count) {
+            mCountOfTimedOutUserMessagesWaitingForConnection = count;
+            return this;
+        }
+
+        /**
+         * Sets countOfTimedOutUserMessagesWaitingForAck value of {@link SatelliteSessionStats}
+         * and then returns the Builder class.
+         */
+        @NonNull
+        public Builder setCountOfTimedOutUserMessagesWaitingForAck(int count) {
+            mCountOfTimedOutUserMessagesWaitingForAck = count;
+            return this;
+        }
+
+        /**
+         * Sets countOfUserMessagesInQueueToBeSent value of {@link SatelliteSessionStats}
+         * and then returns the Builder class.
+         */
+        @NonNull
+        public Builder setCountOfUserMessagesInQueueToBeSent(int count) {
+            mCountOfUserMessagesInQueueToBeSent = count;
+            return this;
+        }
+
+        /** Returns SatelliteSessionStats object. */
+        @NonNull
+        public SatelliteSessionStats build() {
+            return new SatelliteSessionStats(this);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
index 5b9dfc6..b4eb15fd 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
@@ -81,4 +81,12 @@
      * @param supported True means satellite service is supported and false means it is not.
      */
     void onSatelliteSupportedStateChanged(in boolean supported);
+
+    /**
+     * Indicates that the satellite registration failed with following failure code
+     *
+     * @param causeCode the primary failure cause code of the procedure.
+     *        For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+     */
+    void onRegistrationFailure(in int causeCode);
 }
diff --git a/telephony/java/com/android/internal/telephony/IOns.aidl b/telephony/java/com/android/internal/telephony/IOns.aidl
old mode 100755
new mode 100644
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9af73ea..9b01b71 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3390,4 +3390,14 @@
      * @return {@code true} if the setting is successful, {@code false} otherwise.
      */
     boolean setIsSatelliteCommunicationAllowedForCurrentLocationCache(in String state);
+
+    /**
+     * Request to get the session stats of the satellite service.
+     *
+     * @param subId The subId of the subscription to get the session stats for.
+     * @param receiver Result receiver to get the error code of the request and the requested
+     *                 session stats of the satellite service.
+     * @hide
+     */
+    void requestSatelliteSessionStats(int subId, in ResultReceiver receiver);
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index a9ebd5c..2158f3d 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -252,4 +252,10 @@
 
     /** The key to specify the emergency service category */
     public static final String EXTRA_EMERGENCY_SERVICE_CATEGORY = "emergency_service_category";
+
+    /** The key to specify the alternate emergency URNs */
+    public static final String EXTRA_EMERGENCY_URNS = "emergency_urns";
+
+    /** The key to specify whether or not to use emergency routing */
+    public static final String EXTRA_USE_EMERGENCY_ROUTING = "use_emergency_routing";
 }
diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt
index f350957..7d891c8 100644
--- a/test-mock/api/system-current.txt
+++ b/test-mock/api/system-current.txt
@@ -28,7 +28,7 @@
     method @Deprecated public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method @Deprecated public void revokeRuntimePermission(String, String, android.os.UserHandle);
     method @Deprecated public boolean setDefaultBrowserPackageNameAsUser(String, int);
-    method public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String);
+    method @Deprecated public String[] setPackagesSuspended(String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, String);
     method @Deprecated public void setUpdateAvailable(String, boolean);
     method @Deprecated public boolean updateIntentVerificationStatusAsUser(String, int, int);
     method @Deprecated public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
diff --git a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml
old mode 100755
new mode 100644
diff --git a/tests/DozeTest/res/drawable-hdpi/ic_app.png b/tests/DozeTest/res/drawable-hdpi/ic_app.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index a23f211..379b45c 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -24,7 +24,6 @@
 import android.tools.flicker.legacy.LegacyFlickerTest
 import android.tools.flicker.legacy.LegacyFlickerTestFactory
 import android.tools.traces.parsers.toFlickerComponent
-import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -175,12 +174,6 @@
         }
     }
 
-    @FlakyTest(bugId = 342596801)
-    @Test
-    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
-        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-    }
-
     @Ignore("Not applicable to this CUJ.")
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
 
diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index c7da778..c49b509 100644
--- a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -21,6 +21,7 @@
 import android.tools.flicker.legacy.FlickerBuilder
 import android.tools.flicker.legacy.LegacyFlickerTest
 import android.tools.flicker.subject.layers.LayerTraceEntrySubject
+import android.tools.flicker.subject.layers.LayersTraceSubject
 import android.tools.traces.component.ComponentNameMatcher
 import android.tools.traces.component.IComponentMatcher
 import android.tools.traces.surfaceflinger.Display
@@ -46,6 +47,7 @@
         flicker.assertLayers {
             this.visibleLayersShownMoreThanOneConsecutiveEntry(
                 ignoreLayers =
+                    LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
                     listOf(
                         ComponentNameMatcher.SPLASH_SCREEN,
                         ComponentNameMatcher.SNAPSHOT,
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index c0cbdc3..f367c38 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -18,26 +18,31 @@
         "src/**/*.java",
         "src/**/*.kt",
     ],
+    asset_dirs: ["assets"],
     kotlincflags: [
         "-Werror",
     ],
     platform_apis: true,
     certificate: "platform",
     static_libs: [
+        "android.view.flags-aconfig-java",
         "androidx.test.core",
         "androidx.test.ext.junit",
         "androidx.test.ext.truth",
         "androidx.test.rules",
         "androidx.test.runner",
         "androidx.test.uiautomator_uiautomator",
+        "collector-device-lib",
         "compatibility-device-util-axt",
         "cts-input-lib",
+        "cts-wm-util",
         "flag-junit",
         "frameworks-base-testutils",
         "hamcrest-library",
         "kotlin-test",
         "mockito-target-minus-junit4",
         "platform-test-annotations",
+        "platform-screenshot-diff-core",
         "services.core.unboosted",
         "servicestests-utils",
         "testables",
diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml
index 3b723dd..a05d08c 100644
--- a/tests/Input/AndroidManifest.xml
+++ b/tests/Input/AndroidManifest.xml
@@ -22,6 +22,8 @@
     <uses-permission android:name="android.permission.MONITOR_INPUT"/>
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
 
     <application android:label="InputTest" android:debuggable="true">
 
diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml
index f602c512..8db3705 100644
--- a/tests/Input/AndroidTest.xml
+++ b/tests/Input/AndroidTest.xml
@@ -28,4 +28,10 @@
         <!-- Take screenshot upon test failure -->
         <option name="screenshot-on-failure" value="true" />
      </object>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="input_.*" />
+        <!-- Pull files created by tests, like the output of screenshot tests -->
+        <option name="directory-keys" value="/storage/emulated/0/InputTests" />
+        <option name="collect-on-run-ended-only" value="false" />
+    </metrics_collector>
 </configuration>
diff --git a/tests/Input/assets/testPointerFillStyle.png b/tests/Input/assets/testPointerFillStyle.png
new file mode 100644
index 0000000..b2354f8
--- /dev/null
+++ b/tests/Input/assets/testPointerFillStyle.png
Binary files differ
diff --git a/tests/Input/assets/testPointerScale.png b/tests/Input/assets/testPointerScale.png
new file mode 100644
index 0000000..54d37c2
--- /dev/null
+++ b/tests/Input/assets/testPointerScale.png
Binary files differ
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 6b95f5c..8d1fc50 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -29,6 +29,7 @@
 import android.os.SystemClock
 import android.provider.Settings
 import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
+import android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry
 import android.testing.PollingCheck
 
 import androidx.test.uiautomator.By
@@ -36,13 +37,17 @@
 import androidx.test.uiautomator.UiObject2
 import androidx.test.uiautomator.Until
 
+import com.android.cts.input.DebugInputRule
 import com.android.cts.input.UinputTouchScreen
 
+import java.util.concurrent.TimeUnit
+
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -71,6 +76,9 @@
     private val DISPATCHING_TIMEOUT = (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
             Build.HW_TIMEOUT_MULTIPLIER)
 
+    @get:Rule
+    val debugInputRule = DebugInputRule()
+
     @Before
     fun setUp() {
         val contentResolver = instrumentation.targetContext.contentResolver
@@ -86,12 +94,14 @@
     }
 
     @Test
+    @DebugInputRule.DebugInput(bug = 339924248)
     fun testGestureMonitorAnr_Close() {
         triggerAnr()
         clickCloseAppOnAnrDialog()
     }
 
     @Test
+    @DebugInputRule.DebugInput(bug = 339924248)
     fun testGestureMonitorAnr_Wait() {
         triggerAnr()
         clickWaitOnAnrDialog()
@@ -107,7 +117,7 @@
         val closeAppButton: UiObject2? =
                 uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
         if (closeAppButton == null) {
-            fail("Could not find anr dialog")
+            fail("Could not find anr dialog/close button")
             return
         }
         closeAppButton.click()
@@ -183,5 +193,6 @@
         val flags = " -W -n "
         val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity"
         instrumentation.uiAutomation.executeShellCommand(startCmd)
+        waitForStableWindowGeometry(5L, TimeUnit.SECONDS)
     }
 }
diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
new file mode 100644
index 0000000..d196b85
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.test.input
+
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.os.Environment
+import android.view.ContextThemeWrapper
+import android.view.PointerIcon
+import android.view.flags.Flags.enableVectorCursorA11ySettings
+import android.view.flags.Flags.enableVectorCursors
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestName
+import org.junit.runner.RunWith
+import platform.test.screenshot.GoldenPathManager
+import platform.test.screenshot.PathConfig
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.assertAgainstGolden
+import platform.test.screenshot.matchers.BitmapMatcher
+import platform.test.screenshot.matchers.PixelPerfectMatcher
+
+/**
+ * Unit tests for PointerIcon.
+ *
+ * Run with:
+ * atest InputTests:com.android.test.input.PointerIconLoadingTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PointerIconLoadingTest {
+    private lateinit var context: Context
+    private lateinit var exactScreenshotMatcher: BitmapMatcher
+
+    @get:Rule
+    val testName = TestName()
+
+    @get:Rule
+    val screenshotRule = ScreenshotTestRule(GoldenPathManager(
+        InstrumentationRegistry.getInstrumentation().getContext(),
+        ASSETS_PATH,
+        TEST_OUTPUT_PATH,
+        PathConfig()
+    ), disableIconPool = false)
+
+    @Before
+    fun setUp() {
+        context = InstrumentationRegistry.getInstrumentation().targetContext
+        val config =
+            Configuration(context.resources.configuration).apply {
+                densityDpi = DENSITY_DPI
+                screenWidthDp = SCREEN_WIDTH_DP
+                screenHeightDp = SCREEN_HEIGHT_DP
+                smallestScreenWidthDp = SCREEN_WIDTH_DP
+            }
+        context = context.createConfigurationContext(config)
+
+        exactScreenshotMatcher = PixelPerfectMatcher()
+    }
+
+    @Test
+    fun testPointerFillStyle() {
+        assumeTrue(enableVectorCursors())
+        assumeTrue(enableVectorCursorA11ySettings())
+
+        val theme: Resources.Theme = context.getResources().newTheme()
+        theme.setTo(context.getTheme())
+        theme.applyStyle(
+            PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_GREEN),
+            /* force= */ true)
+
+        val pointerIcon =
+            PointerIcon.getLoadedSystemIcon(
+                ContextThemeWrapper(context, theme),
+                PointerIcon.TYPE_ARROW,
+                /* useLargeIcons= */ false,
+                /* pointerScale= */ 1f)
+
+        pointerIcon.getBitmap().assertAgainstGolden(
+            screenshotRule,
+            testName.methodName,
+            exactScreenshotMatcher
+        )
+    }
+
+    @Test
+    fun testPointerScale() {
+        assumeTrue(enableVectorCursors())
+        assumeTrue(enableVectorCursorA11ySettings())
+
+        val pointerScale = 2f
+
+        val pointerIcon =
+            PointerIcon.getLoadedSystemIcon(
+                context,
+                PointerIcon.TYPE_ARROW,
+                /* useLargeIcons= */ false,
+                pointerScale)
+
+        pointerIcon.getBitmap().assertAgainstGolden(
+            screenshotRule,
+            testName.methodName,
+            exactScreenshotMatcher
+        )
+    }
+
+    companion object {
+        const val DENSITY_DPI = 160
+        const val SCREEN_WIDTH_DP = 480
+        const val SCREEN_HEIGHT_DP = 800
+        const val ASSETS_PATH = "tests/input/assets"
+        val TEST_OUTPUT_PATH = Environment.getExternalStorageDirectory().absolutePath +
+                "/InputTests/" +
+                PointerIconLoadingTest::class.java.simpleName
+    }
+}
diff --git a/tests/Internal/TEST_MAPPING b/tests/Internal/TEST_MAPPING
new file mode 100644
index 0000000..20af028
--- /dev/null
+++ b/tests/Internal/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "InternalTests"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/OdmApps/app/AndroidManifest.xml b/tests/OdmApps/app/AndroidManifest.xml
old mode 100755
new mode 100644
diff --git a/tests/OdmApps/priv-app/AndroidManifest.xml b/tests/OdmApps/priv-app/AndroidManifest.xml
old mode 100755
new mode 100644
diff --git a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml
index fa352cf..5058331 100644
--- a/tests/TouchLatency/app/src/main/res/values/styles.xml
+++ b/tests/TouchLatency/app/src/main/res/values/styles.xml
@@ -18,7 +18,7 @@
     <!-- Base application theme. -->
     <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
         <!-- Customize your theme here. -->
-        <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
     </style>
 
 </resources>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
index 56dbde0..fff1dd1 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
@@ -39,11 +39,13 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(BedsteadJUnit4.class)
+@Ignore("b/345557347")
 public final class ConcurrentMultiUserTest {
 
     @ClassRule
diff --git a/packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java b/tests/testables/src/android/animation/AnimatorTestRule.java
similarity index 100%
rename from packages/SystemUI/tests/utils/src/android/animation/AnimatorTestRule.java
rename to tests/testables/src/android/animation/AnimatorTestRule.java
diff --git a/tests/testables/tests/Android.bp b/tests/testables/tests/Android.bp
index 06449e0..d6a4754 100644
--- a/tests/testables/tests/Android.bp
+++ b/tests/testables/tests/Android.bp
@@ -26,14 +26,18 @@
     platform_apis: true,
     srcs: [
         "src/**/*.java",
+        "src/**/*.kt",
         "src/**/I*.aidl",
     ],
     resource_dirs: ["res"],
     static_libs: [
+        "androidx.core_core-animation",
+        "androidx.core_core-ktx",
         "androidx.test.rules",
         "hamcrest-library",
         "mockito-target-inline-minus-junit4",
         "testables",
+        "truth",
     ],
     compile_multilib: "both",
     jni_libs: [
diff --git a/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt b/tests/testables/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
similarity index 94%
rename from packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
rename to tests/testables/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
index f23fbee..5abebee 100644
--- a/packages/SystemUI/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
+++ b/tests/testables/tests/src/android/animation/AnimatorTestRuleIsolationTest.kt
@@ -19,7 +19,6 @@
 import android.testing.TestableLooper.RunWithLooper
 import androidx.core.animation.doOnEnd
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
@@ -32,7 +31,7 @@
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 @RunWithLooper
-class AnimatorTestRuleIsolationTest : SysuiTestCase() {
+class AnimatorTestRuleIsolationTest {
 
     @get:Rule val animatorTestRule = AnimatorTestRule(this)
 
@@ -41,7 +40,7 @@
         // GIVEN global state is reset at the start of the test
         didTouchA = false
         didTouchB = false
-        // WHEN starting 2 animations of different durations, and setting didTouchA at the end
+        // WHEN starting 2 animations of different durations, and setting didTouch{A,B} at the end
         ObjectAnimator.ofFloat(0f, 1f).apply {
             duration = 100
             doOnEnd { didTouchA = true }
@@ -49,7 +48,7 @@
         }
         ObjectAnimator.ofFloat(0f, 1f).apply {
             duration = 150
-            doOnEnd { didTouchA = true }
+            doOnEnd { didTouchB = true }
             start()
         }
         // WHEN when you advance time so that only one of the animations has ended
@@ -65,7 +64,7 @@
         // GIVEN global state is reset at the start of the test
         didTouchA = false
         didTouchB = false
-        // WHEN starting 2 animations of different durations, and setting didTouchB at the end
+        // WHEN starting 2 animations of different durations, and setting didTouch{A,B} at the end
         ObjectAnimator.ofFloat(0f, 1f).apply {
             duration = 100
             doOnEnd { didTouchB = true }
@@ -73,7 +72,7 @@
         }
         ObjectAnimator.ofFloat(0f, 1f).apply {
             duration = 150
-            doOnEnd { didTouchB = true }
+            doOnEnd { didTouchA = true }
             start()
         }
         animatorTestRule.advanceTimeBy(100)
diff --git a/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt b/tests/testables/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
similarity index 96%
rename from packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
rename to tests/testables/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
index fd5f157..9eeaad5 100644
--- a/packages/SystemUI/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
+++ b/tests/testables/tests/src/android/animation/AnimatorTestRulePrecisionTest.kt
@@ -17,10 +17,9 @@
 
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
+import android.view.animation.LinearInterpolator
 import androidx.core.animation.doOnEnd
 import androidx.test.filters.SmallTest
-import com.android.app.animation.Interpolators
-import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
 import org.junit.Rule
 import org.junit.Test
@@ -29,7 +28,7 @@
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 @RunWithLooper
-class AnimatorTestRulePrecisionTest : SysuiTestCase() {
+class AnimatorTestRulePrecisionTest {
 
     @get:Rule val animatorTestRule = AnimatorTestRule(this)
 
@@ -43,7 +42,7 @@
         crossinline onEndAction: (animator: Animator) -> Unit,
     ) {
         ObjectAnimator.ofFloat(this, propertyName, 0f, 1f).also {
-            it.interpolator = Interpolators.LINEAR
+            it.interpolator = LINEAR_INTERPOLATOR
             it.duration = duration
             it.startDelay = startDelay
             it.doOnEnd(onEndAction)
@@ -190,4 +189,8 @@
         assertThat(ended2).isTrue()
         assertThat(AnimationHandler.getAnimationCount()).isEqualTo(0)
     }
+
+    private companion object {
+        private val LINEAR_INTERPOLATOR = LinearInterpolator()
+    }
 }
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
index d551953..c16d18b 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategory.java
@@ -23,6 +23,7 @@
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -40,6 +41,11 @@
         this.mDataTypes = dataTypes;
     }
 
+    public DataCategory(String categoryName) {
+        this.mCategoryName = categoryName;
+        this.mDataTypes = new LinkedHashMap<String, DataType>();
+    }
+
     public String getCategoryName() {
         return mCategoryName;
     }
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
index 90424fe..7244162 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataCategoryFactory.java
@@ -26,33 +26,8 @@
 import java.util.List;
 import java.util.Map;
 
-public class DataCategoryFactory implements AslMarshallableFactory<DataCategory> {
-    @Override
-    public DataCategory createFromHrElements(List<Element> elements) throws MalformedXmlException {
-        String categoryName = null;
-        Map<String, DataType> dataTypeMap = new LinkedHashMap<String, DataType>();
-        for (Element ele : elements) {
-            categoryName = XmlUtils.getStringAttr(ele, XmlUtils.HR_ATTR_DATA_CATEGORY, true);
-            String dataTypeName = XmlUtils.getStringAttr(ele, XmlUtils.HR_ATTR_DATA_TYPE, true);
-            if (!DataTypeConstants.getValidDataTypes().containsKey(categoryName)) {
-                throw new MalformedXmlException(
-                        String.format("Unrecognized data category %s", categoryName));
-            }
-            if (!DataTypeConstants.getValidDataTypes().get(categoryName).contains(dataTypeName)) {
-                throw new MalformedXmlException(
-                        String.format(
-                                "Unrecognized data type name %s for category %s",
-                                dataTypeName, categoryName));
-            }
-            dataTypeMap.put(
-                    dataTypeName, new DataTypeFactory().createFromHrElements(XmlUtils.listOf(ele)));
-        }
-
-        return new DataCategory(categoryName, dataTypeMap);
-    }
-
+public class DataCategoryFactory {
     /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
-    @Override
     public DataCategory createFromOdElements(List<Element> elements) throws MalformedXmlException {
         Element dataCategoryEle = XmlUtils.getSingleElement(elements);
         Map<String, DataType> dataTypeMap = new LinkedHashMap<String, DataType>();
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
index 4a0d759..ba0e3db 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabels.java
@@ -30,28 +30,17 @@
  * DataCategory}
  */
 public class DataLabels implements AslMarshallable {
-    private final Map<String, DataCategory> mDataAccessed;
     private final Map<String, DataCategory> mDataCollected;
     private final Map<String, DataCategory> mDataShared;
 
     public DataLabels(
-            Map<String, DataCategory> dataAccessed,
             Map<String, DataCategory> dataCollected,
             Map<String, DataCategory> dataShared) {
-        mDataAccessed = dataAccessed;
         mDataCollected = dataCollected;
         mDataShared = dataShared;
     }
 
     /**
-     * Returns the data accessed {@link Map} of {@link DataCategoryConstants} to {@link
-     * DataCategory}
-     */
-    public Map<String, DataCategory> getDataAccessed() {
-        return mDataAccessed;
-    }
-
-    /**
      * Returns the data collected {@link Map} of {@link DataCategoryConstants} to {@link
      * DataCategory}
      */
@@ -72,7 +61,6 @@
         Element dataLabelsEle =
                 XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_DATA_LABELS);
 
-        maybeAppendDataUsages(doc, dataLabelsEle, mDataAccessed, XmlUtils.OD_NAME_DATA_ACCESSED);
         maybeAppendDataUsages(doc, dataLabelsEle, mDataCollected, XmlUtils.OD_NAME_DATA_COLLECTED);
         maybeAppendDataUsages(doc, dataLabelsEle, mDataShared, XmlUtils.OD_NAME_DATA_SHARED);
 
@@ -83,9 +71,12 @@
     @Override
     public List<Element> toHrDomElements(Document doc) {
         Element dataLabelsEle = doc.createElement(XmlUtils.HR_TAG_DATA_LABELS);
-        maybeAppendHrDataUsages(doc, dataLabelsEle, mDataAccessed, XmlUtils.HR_TAG_DATA_ACCESSED);
-        maybeAppendHrDataUsages(doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED);
-        maybeAppendHrDataUsages(doc, dataLabelsEle, mDataShared, XmlUtils.HR_TAG_DATA_SHARED);
+        maybeAppendHrDataUsages(
+                doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED, false);
+        maybeAppendHrDataUsages(
+                doc, dataLabelsEle, mDataCollected, XmlUtils.HR_TAG_DATA_COLLECTED_EPHEMERAL, true);
+        maybeAppendHrDataUsages(
+                doc, dataLabelsEle, mDataShared, XmlUtils.HR_TAG_DATA_SHARED, false);
         return XmlUtils.listOf(dataLabelsEle);
     }
 
@@ -115,7 +106,8 @@
             Document doc,
             Element dataLabelsEle,
             Map<String, DataCategory> dataCategoriesMap,
-            String dataUsageTypeName) {
+            String dataUsageTypeName,
+            boolean ephemeral) {
         if (dataCategoriesMap.isEmpty()) {
             return;
         }
@@ -123,10 +115,15 @@
             DataCategory dataCategory = dataCategoriesMap.get(dataCategoryName);
             for (String dataTypeName : dataCategory.getDataTypes().keySet()) {
                 DataType dataType = dataCategory.getDataTypes().get(dataTypeName);
-                // XmlUtils.appendChildren(dataLabelsEle, dataType.toHrDomElements(doc));
+                if (ephemeral
+                        != (dataType.getEphemeral() != null ? dataType.getEphemeral() : false)) {
+                    continue;
+                }
+
                 Element hrDataTypeEle = doc.createElement(dataUsageTypeName);
-                hrDataTypeEle.setAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY, dataCategoryName);
-                hrDataTypeEle.setAttribute(XmlUtils.HR_ATTR_DATA_TYPE, dataTypeName);
+                hrDataTypeEle.setAttribute(
+                        XmlUtils.HR_ATTR_DATA_TYPE,
+                        dataCategoryName + XmlUtils.DATA_TYPE_SEPARATOR + dataTypeName);
                 XmlUtils.maybeSetHrBoolAttr(
                         hrDataTypeEle,
                         XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL,
@@ -135,8 +132,6 @@
                         hrDataTypeEle,
                         XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL,
                         dataType.getIsSharingOptional());
-                XmlUtils.maybeSetHrBoolAttr(
-                        hrDataTypeEle, XmlUtils.HR_ATTR_EPHEMERAL, dataType.getEphemeral());
                 hrDataTypeEle.setAttribute(
                         XmlUtils.HR_ATTR_PURPOSES,
                         String.join(
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
index 5473e01..c4d8876 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataLabelsFactory.java
@@ -18,16 +18,15 @@
 
 import com.android.asllib.util.AslgenUtil;
 import com.android.asllib.util.DataCategoryConstants;
+import com.android.asllib.util.DataTypeConstants;
 import com.android.asllib.util.MalformedXmlException;
 import com.android.asllib.util.XmlUtils;
 
 import org.w3c.dom.Element;
 
-import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 public class DataLabelsFactory implements AslMarshallableFactory<DataLabels> {
 
@@ -39,13 +38,46 @@
             AslgenUtil.logI("Found no DataLabels in hr format.");
             return null;
         }
-        Map<String, DataCategory> dataAccessed =
-                getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_ACCESSED);
         Map<String, DataCategory> dataCollected =
-                getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED);
+                getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED, false);
+        Map<String, DataCategory> dataCollectedEphemeral =
+                getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_COLLECTED_EPHEMERAL, true);
         Map<String, DataCategory> dataShared =
-                getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_SHARED);
-        DataLabels dataLabels = new DataLabels(dataAccessed, dataCollected, dataShared);
+                getDataCategoriesWithTag(ele, XmlUtils.HR_TAG_DATA_SHARED, null);
+
+        for (String dataCollectedEphemeralDataCategoryKey : dataCollectedEphemeral.keySet()) {
+            DataCategory dataCategoryEphemeral =
+                    dataCollectedEphemeral.get(dataCollectedEphemeralDataCategoryKey);
+            for (String dataCollectedEphemeralDataTypeKey :
+                    dataCategoryEphemeral.getDataTypes().keySet()) {
+                if (dataCollected.containsKey(dataCollectedEphemeralDataCategoryKey)
+                        && dataCollected
+                                .get(dataCollectedEphemeralDataCategoryKey)
+                                .getDataTypes()
+                                .containsKey(dataCollectedEphemeralDataTypeKey)) {
+                    throw new MalformedXmlException(
+                            String.format(
+                                    "Duplicate entries in data-collected and"
+                                            + " data-collected-ephemeral: %s %s",
+                                    dataCollectedEphemeralDataCategoryKey,
+                                    dataCollectedEphemeralDataTypeKey));
+                }
+
+                if (!dataCollected.containsKey(dataCollectedEphemeralDataCategoryKey)) {
+                    dataCollected.put(
+                            dataCollectedEphemeralDataCategoryKey,
+                            new DataCategory(dataCollectedEphemeralDataCategoryKey));
+                }
+                DataType dataTypeEphemeral =
+                        dataCategoryEphemeral.getDataTypes().get(dataCollectedEphemeralDataTypeKey);
+                dataCollected
+                        .get(dataCollectedEphemeralDataCategoryKey)
+                        .getDataTypes()
+                        .put(dataCollectedEphemeralDataTypeKey, dataTypeEphemeral);
+            }
+        }
+        DataLabels dataLabels = new DataLabels(dataCollected, dataShared);
+
         validateIsXOptional(dataLabels);
         return dataLabels;
     }
@@ -58,13 +90,11 @@
             AslgenUtil.logI("Found no DataLabels in od format.");
             return null;
         }
-        Map<String, DataCategory> dataAccessed =
-                getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_ACCESSED);
         Map<String, DataCategory> dataCollected =
                 getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_COLLECTED);
         Map<String, DataCategory> dataShared =
                 getOdDataCategoriesWithTag(dataLabelsEle, XmlUtils.OD_NAME_DATA_SHARED);
-        DataLabels dataLabels = new DataLabels(dataAccessed, dataCollected, dataShared);
+        DataLabels dataLabels = new DataLabels(dataCollected, dataShared);
         validateIsXOptional(dataLabels);
         return dataLabels;
     }
@@ -88,56 +118,56 @@
     }
 
     private static Map<String, DataCategory> getDataCategoriesWithTag(
-            Element dataLabelsEle, String dataCategoryUsageTypeTag) throws MalformedXmlException {
+            Element dataLabelsEle, String dataCategoryUsageTypeTag, Boolean ephemeral)
+            throws MalformedXmlException {
         List<Element> dataUsedElements =
                 XmlUtils.getChildrenByTagName(dataLabelsEle, dataCategoryUsageTypeTag);
         Map<String, DataCategory> dataCategoryMap = new LinkedHashMap<String, DataCategory>();
 
-        Set<String> dataCategoryNames = new HashSet<String>();
         for (int i = 0; i < dataUsedElements.size(); i++) {
             Element dataUsedEle = dataUsedElements.get(i);
-            String dataCategoryName = dataUsedEle.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY);
+            String dataCategoryAndTypeCombinedStr =
+                    dataUsedEle.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+            String[] strs = dataCategoryAndTypeCombinedStr.split(XmlUtils.DATA_TYPE_SEPARATOR);
+            if (strs.length != 2) {
+                throw new MalformedXmlException(
+                        String.format(
+                                "Could not parse human-readable data type string (expecting"
+                                        + " substring of _data_type_): %s",
+                                dataCategoryAndTypeCombinedStr));
+            }
+            String dataCategoryName = strs[0];
+            String dataTypeName = strs[1];
+
             if (!DataCategoryConstants.getValidDataCategories().contains(dataCategoryName)) {
                 throw new MalformedXmlException(
                         String.format("Unrecognized category name: %s", dataCategoryName));
             }
-            dataCategoryNames.add(dataCategoryName);
+            if (!DataTypeConstants.getValidDataTypes()
+                    .get(dataCategoryName)
+                    .contains(dataTypeName)) {
+                throw new MalformedXmlException(
+                        String.format(
+                                "Unrecognized data type name %s for category %s",
+                                dataTypeName, dataCategoryName));
+            }
+
+            if (!dataCategoryMap.containsKey(dataCategoryName)) {
+                dataCategoryMap.put(dataCategoryName, new DataCategory(dataCategoryName));
+            }
+            dataCategoryMap
+                    .get(dataCategoryName)
+                    .getDataTypes()
+                    .put(
+                            dataTypeName,
+                            new DataTypeFactory().createFromHrElements(dataUsedEle, ephemeral));
         }
-        for (String dataCategoryName : dataCategoryNames) {
-            var dataCategoryElements =
-                    dataUsedElements.stream()
-                            .filter(
-                                    ele ->
-                                            ele.getAttribute(XmlUtils.HR_ATTR_DATA_CATEGORY)
-                                                    .equals(dataCategoryName))
-                            .toList();
-            DataCategory dataCategory =
-                    new DataCategoryFactory().createFromHrElements(dataCategoryElements);
-            dataCategoryMap.put(dataCategoryName, dataCategory);
-        }
+
         return dataCategoryMap;
     }
 
     private void validateIsXOptional(DataLabels dataLabels) throws MalformedXmlException {
         // Validate booleans such as isCollectionOptional, isSharingOptional.
-        for (DataCategory dataCategory : dataLabels.getDataAccessed().values()) {
-            for (DataType dataType : dataCategory.getDataTypes().values()) {
-                if (dataType.getIsSharingOptional() != null) {
-                    throw new MalformedXmlException(
-                            String.format(
-                                    "isSharingOptional was unexpectedly defined on a DataType"
-                                            + " belonging to data accessed: %s",
-                                    dataType.getDataTypeName()));
-                }
-                if (dataType.getIsCollectionOptional() != null) {
-                    throw new MalformedXmlException(
-                            String.format(
-                                    "isCollectionOptional was unexpectedly defined on a DataType"
-                                            + " belonging to data accessed: %s",
-                                    dataType.getDataTypeName()));
-                }
-            }
-        }
         for (DataCategory dataCategory : dataLabels.getDataCollected().values()) {
             for (DataType dataType : dataCategory.getDataTypes().values()) {
                 if (dataType.getIsSharingOptional() != null) {
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
index 488c259..a5559d8 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/DataTypeFactory.java
@@ -25,12 +25,22 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-public class DataTypeFactory implements AslMarshallableFactory<DataType> {
+public class DataTypeFactory {
     /** Creates a {@link DataType} from the human-readable DOM element. */
-    @Override
-    public DataType createFromHrElements(List<Element> elements) throws MalformedXmlException {
-        Element hrDataTypeEle = XmlUtils.getSingleElement(elements);
-        String dataTypeName = hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+    public DataType createFromHrElements(Element hrDataTypeEle, Boolean ephemeral)
+            throws MalformedXmlException {
+        String dataCategoryAndTypeCombinedStr =
+                hrDataTypeEle.getAttribute(XmlUtils.HR_ATTR_DATA_TYPE);
+        String[] strs = dataCategoryAndTypeCombinedStr.split(XmlUtils.DATA_TYPE_SEPARATOR);
+        if (strs.length != 2) {
+            throw new MalformedXmlException(
+                    String.format(
+                            "Could not parse human-readable data type string (expecting substring"
+                                    + " of _data_type_): %s",
+                            dataCategoryAndTypeCombinedStr));
+        }
+        String dataTypeName = strs[1];
+
         List<DataType.Purpose> purposes =
                 XmlUtils.getPipelineSplitAttr(hrDataTypeEle, XmlUtils.HR_ATTR_PURPOSES, true)
                         .stream()
@@ -47,13 +57,13 @@
                 XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_IS_COLLECTION_OPTIONAL, false);
         Boolean isSharingOptional =
                 XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_IS_SHARING_OPTIONAL, false);
-        Boolean ephemeral = XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_EPHEMERAL, false);
+        // Boolean ephemeral = XmlUtils.getBoolAttr(hrDataTypeEle, XmlUtils.HR_ATTR_EPHEMERAL,
+        // false);
         return new DataType(
                 dataTypeName, purposes, isCollectionOptional, isSharingOptional, ephemeral);
     }
 
     /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
-    @Override
     public DataType createFromOdElements(List<Element> elements) throws MalformedXmlException {
         Element odDataTypeEle = XmlUtils.getSingleElement(elements);
         String dataTypeName = odDataTypeEle.getAttribute(XmlUtils.OD_ATTR_NAME);
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
index 854c0d0..242e7be 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabel.java
@@ -26,15 +26,10 @@
 /** Safety Label representation containing zero or more {@link DataCategory} for data shared */
 public class SystemAppSafetyLabel implements AslMarshallable {
 
-    private final String mUrl;
+    private final Boolean mDeclaration;
 
-    public SystemAppSafetyLabel(String url) {
-        this.mUrl = url;
-    }
-
-    /** Returns the system app safety label URL. */
-    public String getUrl() {
-        return mUrl;
+    public SystemAppSafetyLabel(Boolean d) {
+        this.mDeclaration = d;
     }
 
     /** Creates an on-device DOM element from the {@link SystemAppSafetyLabel}. */
@@ -43,7 +38,7 @@
         Element systemAppSafetyLabelEle =
                 XmlUtils.createPbundleEleWithName(doc, XmlUtils.OD_NAME_SYSTEM_APP_SAFETY_LABEL);
         systemAppSafetyLabelEle.appendChild(
-                XmlUtils.createOdStringEle(doc, XmlUtils.OD_NAME_URL, mUrl));
+                XmlUtils.createOdBooleanEle(doc, XmlUtils.OD_NAME_DECLARATION, mDeclaration));
         return XmlUtils.listOf(systemAppSafetyLabelEle);
     }
 
@@ -52,7 +47,8 @@
     public List<Element> toHrDomElements(Document doc) {
         Element systemAppSafetyLabelEle =
                 doc.createElement(XmlUtils.HR_TAG_SYSTEM_APP_SAFETY_LABEL);
-        systemAppSafetyLabelEle.setAttribute(XmlUtils.HR_ATTR_URL, mUrl);
+        XmlUtils.maybeSetHrBoolAttr(
+                systemAppSafetyLabelEle, XmlUtils.HR_ATTR_DECLARATION, mDeclaration);
         return XmlUtils.listOf(systemAppSafetyLabelEle);
     }
 }
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
index c8e22b6..7f4aa7a 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/marshallable/SystemAppSafetyLabelFactory.java
@@ -36,8 +36,9 @@
             return null;
         }
 
-        String url = XmlUtils.getStringAttr(systemAppSafetyLabelEle, XmlUtils.HR_ATTR_URL, true);
-        return new SystemAppSafetyLabel(url);
+        Boolean declaration =
+                XmlUtils.getBoolAttr(systemAppSafetyLabelEle, XmlUtils.HR_ATTR_DECLARATION, true);
+        return new SystemAppSafetyLabel(declaration);
     }
 
     /** Creates an {@link AslMarshallableFactory} from on-device DOM elements */
@@ -49,7 +50,8 @@
             AslgenUtil.logI("No SystemAppSafetyLabel found in od format.");
             return null;
         }
-        String url = XmlUtils.getOdStringEle(systemAppSafetyLabelEle, XmlUtils.OD_NAME_URL, true);
-        return new SystemAppSafetyLabel(url);
+        Boolean declaration =
+                XmlUtils.getOdBoolEle(systemAppSafetyLabelEle, XmlUtils.OD_NAME_DECLARATION, true);
+        return new SystemAppSafetyLabel(declaration);
     }
 }
diff --git a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
index 1d54ead..97cbc39 100644
--- a/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
+++ b/tools/app_metadata_bundles/src/lib/java/com/android/asllib/util/XmlUtils.java
@@ -27,6 +27,8 @@
 import java.util.List;
 
 public class XmlUtils {
+    public static final String DATA_TYPE_SEPARATOR = "_data_type_";
+
     public static final String HR_TAG_APP_METADATA_BUNDLES = "app-metadata-bundles";
     public static final String HR_TAG_SYSTEM_APP_SAFETY_LABEL = "system-app-safety-label";
     public static final String HR_TAG_SAFETY_LABELS = "safety-labels";
@@ -38,6 +40,7 @@
     public static final String HR_TAG_THIRD_PARTY_VERIFICATION = "third-party-verification";
     public static final String HR_TAG_DATA_ACCESSED = "data-accessed";
     public static final String HR_TAG_DATA_COLLECTED = "data-collected";
+    public static final String HR_TAG_DATA_COLLECTED_EPHEMERAL = "data-collected-ephemeral";
     public static final String HR_TAG_DATA_SHARED = "data-shared";
     public static final String HR_ATTR_NAME = "name";
     public static final String HR_ATTR_EMAIL = "email";
@@ -52,10 +55,11 @@
     public static final String HR_ATTR_IS_SHARING_OPTIONAL = "isSharingOptional";
     public static final String HR_ATTR_IS_DATA_DELETABLE = "isDataDeletable";
     public static final String HR_ATTR_IS_DATA_ENCRYPTED = "isDataEncrypted";
-    public static final String HR_ATTR_EPHEMERAL = "ephemeral";
+    // public static final String HR_ATTR_EPHEMERAL = "ephemeral";
     public static final String HR_ATTR_PURPOSES = "purposes";
     public static final String HR_ATTR_VERSION = "version";
     public static final String HR_ATTR_URL = "url";
+    public static final String HR_ATTR_DECLARATION = "declaration";
     public static final String HR_ATTR_TITLE = "title";
     public static final String HR_ATTR_DESCRIPTION = "description";
     public static final String HR_ATTR_CONTAINS_ADS = "containsAds";
@@ -103,6 +107,7 @@
     public static final String OD_NAME_CATEGORY = "category";
     public static final String OD_NAME_VERSION = "version";
     public static final String OD_NAME_URL = "url";
+    public static final String OD_NAME_DECLARATION = "declaration";
     public static final String OD_NAME_SYSTEM_APP_SAFETY_LABEL = "system_app_safety_label";
     public static final String OD_NAME_SECURITY_LABELS = "security_labels";
     public static final String OD_NAME_THIRD_PARTY_VERIFICATION = "third_party_verification";
@@ -299,12 +304,13 @@
                         .toList();
         if (boolEles.size() > 1) {
             throw new MalformedXmlException(
-                    String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+                    String.format(
+                            "Found more than one boolean %s in %s.", nameName, ele.getTagName()));
         }
         if (boolEles.isEmpty()) {
             if (required) {
                 throw new MalformedXmlException(
-                        String.format("Found no %s in %s.", nameName, ele.getTagName()));
+                        String.format("Found no boolean %s in %s.", nameName, ele.getTagName()));
             }
             return null;
         }
@@ -329,12 +335,13 @@
                         .toList();
         if (longEles.size() > 1) {
             throw new MalformedXmlException(
-                    String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+                    String.format(
+                            "Found more than one long %s in %s.", nameName, ele.getTagName()));
         }
         if (longEles.isEmpty()) {
             if (required) {
                 throw new MalformedXmlException(
-                        String.format("Found no %s in %s.", nameName, ele.getTagName()));
+                        String.format("Found no long %s in %s.", nameName, ele.getTagName()));
             }
             return null;
         }
@@ -359,12 +366,13 @@
                         .toList();
         if (eles.size() > 1) {
             throw new MalformedXmlException(
-                    String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+                    String.format(
+                            "Found more than one string %s in %s.", nameName, ele.getTagName()));
         }
         if (eles.isEmpty()) {
             if (required) {
                 throw new MalformedXmlException(
-                        String.format("Found no %s in %s.", nameName, ele.getTagName()));
+                        String.format("Found no string %s in %s.", nameName, ele.getTagName()));
             }
             return null;
         }
@@ -386,12 +394,13 @@
                         .toList();
         if (eles.size() > 1) {
             throw new MalformedXmlException(
-                    String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+                    String.format(
+                            "Found more than one pbundle %s in %s.", nameName, ele.getTagName()));
         }
         if (eles.isEmpty()) {
             if (required) {
                 throw new MalformedXmlException(
-                        String.format("Found no %s in %s.", nameName, ele.getTagName()));
+                        String.format("Found no pbundle %s in %s.", nameName, ele.getTagName()));
             }
             return null;
         }
@@ -456,12 +465,15 @@
                         .toList();
         if (arrayEles.size() > 1) {
             throw new MalformedXmlException(
-                    String.format("Found more than one %s in %s.", nameName, ele.getTagName()));
+                    String.format(
+                            "Found more than one string array %s in %s.",
+                            nameName, ele.getTagName()));
         }
         if (arrayEles.isEmpty()) {
             if (required) {
                 throw new MalformedXmlException(
-                        String.format("Found no %s in %s.", nameName, ele.getTagName()));
+                        String.format(
+                                "Found no string array %s in %s.", nameName, ele.getTagName()));
             }
             return null;
         }
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
index f156484..dbeeb49 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AllTests.java
@@ -18,7 +18,6 @@
 
 import com.android.asllib.marshallable.AndroidSafetyLabelTest;
 import com.android.asllib.marshallable.AppInfoTest;
-import com.android.asllib.marshallable.DataCategoryTest;
 import com.android.asllib.marshallable.DataLabelsTest;
 import com.android.asllib.marshallable.DataTypeEqualityTest;
 import com.android.asllib.marshallable.DeveloperInfoTest;
@@ -36,7 +35,7 @@
     AslgenTests.class,
     AndroidSafetyLabelTest.class,
     AppInfoTest.class,
-    DataCategoryTest.class,
+    // DataCategoryTest.class,
     DataLabelsTest.class,
     DataTypeEqualityTest.class,
     DeveloperInfoTest.class,
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
index d2e0fc3..5d1d45a 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/AslgenTests.java
@@ -34,8 +34,7 @@
 @RunWith(JUnit4.class)
 public class AslgenTests {
     private static final String VALID_MAPPINGS_PATH = "com/android/asllib/validmappings";
-    private static final List<String> VALID_MAPPINGS_SUBDIRS =
-            List.of("location", "contacts", "general");
+    private static final List<String> VALID_MAPPINGS_SUBDIRS = List.of("general");
     private static final String HR_XML_FILENAME = "hr.xml";
     private static final String OD_XML_FILENAME = "od.xml";
 
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java
deleted file mode 100644
index ebb3186..0000000
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataCategoryTest.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.asllib.marshallable;
-
-import com.android.asllib.testutils.TestUtils;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class DataCategoryTest {
-    private static final String DATA_CATEGORY_HR_PATH = "com/android/asllib/datacategory/hr";
-    private static final String DATA_CATEGORY_OD_PATH = "com/android/asllib/datacategory/od";
-
-    private static final String VALID_PERSONAL_FILE_NAME = "data-category-personal.xml";
-    private static final String VALID_PARTIAL_PERSONAL_FILE_NAME =
-            "data-category-personal-partial.xml";
-    private static final String VALID_FINANCIAL_FILE_NAME = "data-category-financial.xml";
-    private static final String VALID_LOCATION_FILE_NAME = "data-category-location.xml";
-    private static final String VALID_EMAIL_TEXT_MESSAGE_FILE_NAME =
-            "data-category-email-text-message.xml";
-    private static final String VALID_PHOTO_VIDEO_FILE_NAME = "data-category-photo-video.xml";
-    private static final String VALID_AUDIO_FILE_NAME = "data-category-audio.xml";
-    private static final String VALID_STORAGE_FILE_NAME = "data-category-storage.xml";
-    private static final String VALID_HEALTH_FITNESS_FILE_NAME = "data-category-health-fitness.xml";
-    private static final String VALID_CONTACTS_FILE_NAME = "data-category-contacts.xml";
-    private static final String VALID_CALENDAR_FILE_NAME = "data-category-calendar.xml";
-    private static final String VALID_IDENTIFIERS_FILE_NAME = "data-category-identifiers.xml";
-    private static final String VALID_APP_PERFORMANCE_FILE_NAME =
-            "data-category-app-performance.xml";
-    private static final String VALID_ACTIONS_IN_APP_FILE_NAME = "data-category-actions-in-app.xml";
-    private static final String VALID_SEARCH_AND_BROWSING_FILE_NAME =
-            "data-category-search-and-browsing.xml";
-
-    private static final String EMPTY_PURPOSE_PERSONAL_FILE_NAME =
-            "data-category-personal-empty-purpose.xml";
-    private static final String MISSING_PURPOSE_PERSONAL_FILE_NAME =
-            "data-category-personal-missing-purpose.xml";
-    private static final String UNRECOGNIZED_TYPE_PERSONAL_FILE_NAME =
-            "data-category-personal-unrecognized-type.xml";
-    private static final String UNRECOGNIZED_CATEGORY_FILE_NAME = "data-category-unrecognized.xml";
-
-    /** Logic for setting up tests (empty if not yet needed). */
-    public static void main(String[] params) throws Exception {}
-
-    @Before
-    public void setUp() throws Exception {
-        System.out.println("set up.");
-    }
-
-    /** Test for data category personal. */
-    @Test
-    public void testDataCategoryPersonal() throws Exception {
-        System.out.println("starting testDataCategoryPersonal.");
-        testHrToOdDataCategory(VALID_PERSONAL_FILE_NAME);
-    }
-
-    /** Test for data category financial. */
-    @Test
-    public void testDataCategoryFinancial() throws Exception {
-        System.out.println("starting testDataCategoryFinancial.");
-        testHrToOdDataCategory(VALID_FINANCIAL_FILE_NAME);
-    }
-
-    /** Test for data category location. */
-    @Test
-    public void testDataCategoryLocation() throws Exception {
-        System.out.println("starting testDataCategoryLocation.");
-        testHrToOdDataCategory(VALID_LOCATION_FILE_NAME);
-    }
-
-    /** Test for data category email text message. */
-    @Test
-    public void testDataCategoryEmailTextMessage() throws Exception {
-        System.out.println("starting testDataCategoryEmailTextMessage.");
-        testHrToOdDataCategory(VALID_EMAIL_TEXT_MESSAGE_FILE_NAME);
-    }
-
-    /** Test for data category photo video. */
-    @Test
-    public void testDataCategoryPhotoVideo() throws Exception {
-        System.out.println("starting testDataCategoryPhotoVideo.");
-        testHrToOdDataCategory(VALID_PHOTO_VIDEO_FILE_NAME);
-    }
-
-    /** Test for data category audio. */
-    @Test
-    public void testDataCategoryAudio() throws Exception {
-        System.out.println("starting testDataCategoryAudio.");
-        testHrToOdDataCategory(VALID_AUDIO_FILE_NAME);
-    }
-
-    /** Test for data category storage. */
-    @Test
-    public void testDataCategoryStorage() throws Exception {
-        System.out.println("starting testDataCategoryStorage.");
-        testHrToOdDataCategory(VALID_STORAGE_FILE_NAME);
-    }
-
-    /** Test for data category health fitness. */
-    @Test
-    public void testDataCategoryHealthFitness() throws Exception {
-        System.out.println("starting testDataCategoryHealthFitness.");
-        testHrToOdDataCategory(VALID_HEALTH_FITNESS_FILE_NAME);
-    }
-
-    /** Test for data category contacts. */
-    @Test
-    public void testDataCategoryContacts() throws Exception {
-        System.out.println("starting testDataCategoryContacts.");
-        testHrToOdDataCategory(VALID_CONTACTS_FILE_NAME);
-    }
-
-    /** Test for data category calendar. */
-    @Test
-    public void testDataCategoryCalendar() throws Exception {
-        System.out.println("starting testDataCategoryCalendar.");
-        testHrToOdDataCategory(VALID_CALENDAR_FILE_NAME);
-    }
-
-    /** Test for data category identifiers. */
-    @Test
-    public void testDataCategoryIdentifiers() throws Exception {
-        System.out.println("starting testDataCategoryIdentifiers.");
-        testHrToOdDataCategory(VALID_IDENTIFIERS_FILE_NAME);
-    }
-
-    /** Test for data category app performance. */
-    @Test
-    public void testDataCategoryAppPerformance() throws Exception {
-        System.out.println("starting testDataCategoryAppPerformance.");
-        testHrToOdDataCategory(VALID_APP_PERFORMANCE_FILE_NAME);
-    }
-
-    /** Test for data category actions in app. */
-    @Test
-    public void testDataCategoryActionsInApp() throws Exception {
-        System.out.println("starting testDataCategoryActionsInApp.");
-        testHrToOdDataCategory(VALID_ACTIONS_IN_APP_FILE_NAME);
-    }
-
-    /** Test for data category search and browsing. */
-    @Test
-    public void testDataCategorySearchAndBrowsing() throws Exception {
-        System.out.println("starting testDataCategorySearchAndBrowsing.");
-        testHrToOdDataCategory(VALID_SEARCH_AND_BROWSING_FILE_NAME);
-    }
-
-    /** Test for data category search and browsing. */
-    @Test
-    public void testMissingOptionalsAllowed() throws Exception {
-        System.out.println("starting testMissingOptionalsAllowed.");
-        testHrToOdDataCategory(VALID_PARTIAL_PERSONAL_FILE_NAME);
-    }
-
-    /** Test for empty purposes. */
-    @Test
-    public void testEmptyPurposesNotAllowed() throws Exception {
-        System.out.println("starting testEmptyPurposesNotAllowed.");
-        hrToOdExpectException(EMPTY_PURPOSE_PERSONAL_FILE_NAME);
-    }
-
-    /** Test for missing purposes. */
-    @Test
-    public void testMissingPurposesNotAllowed() throws Exception {
-        System.out.println("starting testMissingPurposesNotAllowed.");
-        hrToOdExpectException(MISSING_PURPOSE_PERSONAL_FILE_NAME);
-    }
-
-    /** Test for unrecognized type. */
-    @Test
-    public void testUnrecognizedTypeNotAllowed() throws Exception {
-        System.out.println("starting testUnrecognizedTypeNotAllowed.");
-        hrToOdExpectException(UNRECOGNIZED_TYPE_PERSONAL_FILE_NAME);
-    }
-
-    /** Test for unrecognized category. */
-    @Test
-    public void testUnrecognizedCategoryNotAllowed() throws Exception {
-        System.out.println("starting testUnrecognizedCategoryNotAllowed.");
-        hrToOdExpectException(UNRECOGNIZED_CATEGORY_FILE_NAME);
-    }
-
-    private void hrToOdExpectException(String fileName) {
-        TestUtils.hrToOdExpectException(new DataCategoryFactory(), DATA_CATEGORY_HR_PATH, fileName);
-    }
-
-    private void testHrToOdDataCategory(String fileName) throws Exception {
-        TestUtils.testHrToOd(
-                TestUtils.document(),
-                new DataCategoryFactory(),
-                DATA_CATEGORY_HR_PATH,
-                DATA_CATEGORY_OD_PATH,
-                fileName);
-    }
-}
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
index 2661726..ff43741 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/DataLabelsTest.java
@@ -34,6 +34,10 @@
             "data-labels-accessed-invalid-bool.xml";
     private static final String COLLECTED_VALID_BOOL_FILE_NAME =
             "data-labels-collected-valid-bool.xml";
+    private static final String COLLECTED_EPHEMERAL_FILE_NAME =
+            "data-labels-collected-ephemeral.xml";
+    private static final String COLLECTED_EPHEMERAL_COLLISION_FILE_NAME =
+            "data-labels-collected-ephemeral-collision.xml";
     private static final String COLLECTED_INVALID_BOOL_FILE_NAME =
             "data-labels-collected-invalid-bool.xml";
     private static final String SHARED_VALID_BOOL_FILE_NAME = "data-labels-shared-valid-bool.xml";
@@ -69,21 +73,6 @@
         System.out.println("set up.");
     }
 
-    /** Test for data labels accessed valid bool. */
-    @Test
-    public void testDataLabelsAccessedValidBool() throws Exception {
-        System.out.println("starting testDataLabelsAccessedValidBool.");
-        testHrToOdDataLabels(ACCESSED_VALID_BOOL_FILE_NAME);
-        testOdToHrDataLabels(ACCESSED_VALID_BOOL_FILE_NAME);
-    }
-
-    /** Test for data labels accessed invalid bool. */
-    @Test
-    public void testDataLabelsAccessedInvalidBool() throws Exception {
-        System.out.println("starting testDataLabelsAccessedInvalidBool.");
-        hrToOdExpectException(ACCESSED_INVALID_BOOL_FILE_NAME);
-    }
-
     /** Test for data labels collected valid bool. */
     @Test
     public void testDataLabelsCollectedValidBool() throws Exception {
@@ -92,6 +81,21 @@
         testOdToHrDataLabels(COLLECTED_VALID_BOOL_FILE_NAME);
     }
 
+    /** Test for data labels collected ephemeral. */
+    @Test
+    public void testDataLabelsCollectedEphemeral() throws Exception {
+        System.out.println("starting testDataLabelsCollectedEphemeral.");
+        testHrToOdDataLabels(COLLECTED_EPHEMERAL_FILE_NAME);
+        testOdToHrDataLabels(COLLECTED_EPHEMERAL_FILE_NAME);
+    }
+
+    /** Test for data labels ephemeral collision. */
+    @Test
+    public void testDataLabelsCollectedEphemeralCollision() throws Exception {
+        System.out.println("starting testDataLabelsCollectedEphemeralCollision.");
+        hrToOdExpectException(COLLECTED_EPHEMERAL_COLLISION_FILE_NAME);
+    }
+
     /** Test for data labels collected invalid bool. */
     @Test
     public void testDataLabelsCollectedInvalidBool() throws Exception {
diff --git a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
index 33c2764..87d3e44 100644
--- a/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
+++ b/tools/app_metadata_bundles/src/test/java/com/android/asllib/marshallable/SystemAppSafetyLabelTest.java
@@ -31,7 +31,7 @@
             "com/android/asllib/systemappsafetylabel/od";
 
     private static final String VALID_FILE_NAME = "valid.xml";
-    private static final String MISSING_URL_FILE_NAME = "missing-url.xml";
+    private static final String MISSING_BOOL_FILE_NAME = "missing-bool.xml";
 
     /** Logic for setting up tests (empty if not yet needed). */
     public static void main(String[] params) throws Exception {}
@@ -49,12 +49,12 @@
         testOdToHrSystemAppSafetyLabel(VALID_FILE_NAME);
     }
 
-    /** Tests missing url. */
+    /** Tests missing bool. */
     @Test
-    public void testMissingUrl() throws Exception {
-        System.out.println("starting testMissingUrl.");
-        hrToOdExpectException(MISSING_URL_FILE_NAME);
-        odToHrExpectException(MISSING_URL_FILE_NAME);
+    public void testMissingBool() throws Exception {
+        System.out.println("starting testMissingBool.");
+        hrToOdExpectException(MISSING_BOOL_FILE_NAME);
+        odToHrExpectException(MISSING_BOOL_FILE_NAME);
     }
 
     private void hrToOdExpectException(String fileName) {
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml
index 7bcde45..afb0486 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/hr/with-system-app-safety-label.xml
@@ -1,4 +1,4 @@
 <app-metadata-bundles version="123456">
-<system-app-safety-label url="www.example.com">
+<system-app-safety-label declaration="true">
 </system-app-safety-label>
 </app-metadata-bundles>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml
index ef0f549..e8640c4 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/androidsafetylabel/od/with-system-app-safety-label.xml
@@ -1,6 +1,6 @@
 <bundle>
     <long name="version" value="123456"/>
     <pbundle_as_map name="system_app_safety_label">
-        <string name="url" value="www.example.com"/>
+        <boolean name="declaration" value="true"/>
     </pbundle_as_map>
 </bundle>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
index 68e191e..680e01a 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-actions-in-app.xml
@@ -1,17 +1,12 @@
 <data-labels>
-    <data-shared dataCategory="actions_in_app"
-        dataType="user_interaction"
+    <data-shared dataType="actions_in_app_data_type_user_interaction"
         purposes="analytics" />
-    <data-shared dataCategory="actions_in_app"
-        dataType="in_app_search_history"
+    <data-shared dataType="actions_in_app_data_type_in_app_search_history"
         purposes="analytics" />
-    <data-shared dataCategory="actions_in_app"
-        dataType="installed_apps"
+    <data-shared dataType="actions_in_app_data_type_installed_apps"
         purposes="analytics" />
-    <data-shared dataCategory="actions_in_app"
-        dataType="user_generated_content"
+    <data-shared dataType="actions_in_app_data_type_user_generated_content"
         purposes="analytics" />
-    <data-shared dataCategory="actions_in_app"
-        dataType="other"
+    <data-shared dataType="actions_in_app_data_type_other"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
index a6bd17d..db114bf 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-app-performance.xml
@@ -1,11 +1,8 @@
 <data-labels>
-    <data-shared dataCategory="app_performance"
-        dataType="crash_logs"
+    <data-shared dataType="app_performance_data_type_crash_logs"
         purposes="analytics" />
-    <data-shared dataCategory="app_performance"
-        dataType="performance_diagnostics"
+    <data-shared dataType="app_performance_data_type_performance_diagnostics"
         purposes="analytics" />
-    <data-shared dataCategory="app_performance"
-        dataType="other"
+    <data-shared dataType="app_performance_data_type_other"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
index 6274604..cf273f4 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-audio.xml
@@ -1,11 +1,8 @@
 <data-labels>
-    <data-shared dataCategory="audio"
-        dataType="sound_recordings"
+    <data-shared dataType="audio_data_type_sound_recordings"
         purposes="analytics" />
-    <data-shared dataCategory="audio"
-        dataType="music_files"
+    <data-shared dataType="audio_data_type_music_files"
         purposes="analytics" />
-    <data-shared dataCategory="audio"
-        dataType="other"
+    <data-shared dataType="audio_data_type_other"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
index f7201f6..16f9d9b6 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-calendar.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="calendar"
-        dataType="calendar"
+    <data-shared dataType="calendar_data_type_calendar"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
index e8d40be..6d7a4e8 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-contacts.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="contacts"
-        dataType="contacts"
+    <data-shared dataType="contacts_data_type_contacts"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
index 69e9b87..7a9e978 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-email-text-message.xml
@@ -1,11 +1,8 @@
 <data-labels>
-    <data-shared dataCategory="email_text_message"
-        dataType="emails"
+    <data-shared dataType="email_text_message_data_type_emails"
         purposes="analytics" />
-    <data-shared dataCategory="email_text_message"
-        dataType="text_messages"
+    <data-shared dataType="email_text_message_data_type_text_messages"
         purposes="analytics" />
-    <data-shared dataCategory="email_text_message"
-        dataType="other"
+    <data-shared dataType="email_text_message_data_type_other"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
index fdd8456..24385b6 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-financial.xml
@@ -1,14 +1,10 @@
 <data-labels>
-    <data-shared dataCategory="financial"
-        dataType="card_bank_account"
+    <data-shared dataType="financial_data_type_card_bank_account"
         purposes="analytics" />
-    <data-shared dataCategory="financial"
-    dataType="purchase_history"
+    <data-shared dataType="financial_data_type_purchase_history"
     purposes="analytics" />
-    <data-shared dataCategory="financial"
-        dataType="credit_score"
+    <data-shared dataType="financial_data_type_credit_score"
         purposes="analytics" />
-    <data-shared dataCategory="financial"
-        dataType="other"
+    <data-shared dataType="financial_data_type_other"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
index bac58e6..faf30b0 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-health-fitness.xml
@@ -1,8 +1,6 @@
 <data-labels>
-    <data-shared dataCategory="health_fitness"
-        dataType="health"
+    <data-shared dataType="health_fitness_data_type_health"
         purposes="analytics" />
-    <data-shared dataCategory="health_fitness"
-        dataType="fitness"
+    <data-shared dataType="health_fitness_data_type_fitness"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
index ee45f26..5101906 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-identifiers.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="identifiers"
-        dataType="other"
+    <data-shared dataType="identifiers_data_type_other"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
index e8e5911..72cda7e 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-location.xml
@@ -1,8 +1,6 @@
 <data-labels>
-    <data-shared dataCategory="location"
-        dataType="approx_location"
+    <data-shared dataType="location_data_type_approx_location"
         purposes="analytics" />
-    <data-shared dataCategory="location"
-        dataType="precise_location"
+    <data-shared dataType="location_data_type_precise_location"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
index 0b220f4..2558681 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-empty-purpose.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="personal"
-    dataType="email_address"
+    <data-shared dataType="personal_data_type_email_address"
     purposes="" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
index ac221f2..c5a5475 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-missing-purpose.xml
@@ -1,4 +1,3 @@
 <data-labels>
-    <data-shared dataCategory="personal"
-    dataType="email_address" />
+    <data-shared dataType="personal_data_type_email_address" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
index 11b7368..6ccf336 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-partial.xml
@@ -1,8 +1,6 @@
 <data-labels>
-    <data-shared dataCategory="personal"
-        dataType="name"
+    <data-shared dataType="personal_data_type_name"
         purposes="analytics|developer_communications" />
-    <data-shared dataCategory="personal"
-    dataType="email_address"
+    <data-shared dataType="personal_data_type_email_address"
     purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
index f1fbd56..bd88ada 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal-unrecognized-type.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="personal"
-    dataType="unrecognized"
+    <data-shared dataType="personal_data_type_unrecognized"
     purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
index 5907462..742ed86 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-personal.xml
@@ -1,31 +1,21 @@
 <data-labels>
-    <data-shared dataCategory="personal"
-        dataType="name"
-        ephemeral="true"
+    <data-shared dataType="personal_data_type_name"
         isSharingOptional="true"
         purposes="analytics|developer_communications" />
-    <data-shared dataCategory="personal"
-    dataType="email_address"
+    <data-shared dataType="personal_data_type_email_address"
     purposes="analytics" />
-    <data-shared dataCategory="personal"
-    dataType="physical_address"
+    <data-shared dataType="personal_data_type_physical_address"
     purposes="analytics" />
-    <data-shared dataCategory="personal"
-    dataType="phone_number"
+    <data-shared dataType="personal_data_type_phone_number"
     purposes="analytics" />
-    <data-shared dataCategory="personal"
-    dataType="race_ethnicity"
+    <data-shared dataType="personal_data_type_race_ethnicity"
     purposes="analytics" />
-    <data-shared dataCategory="personal"
-    dataType="political_or_religious_beliefs"
+    <data-shared dataType="personal_data_type_political_or_religious_beliefs"
     purposes="analytics" />
-    <data-shared dataCategory="personal"
-    dataType="sexual_orientation_or_gender_identity"
+    <data-shared dataType="personal_data_type_sexual_orientation_or_gender_identity"
     purposes="analytics" />
-    <data-shared dataCategory="personal"
-    dataType="personal_identifiers"
+    <data-shared dataType="personal_data_type_personal_identifiers"
     purposes="analytics" />
-    <data-shared dataCategory="personal"
-    dataType="other"
+    <data-shared dataType="personal_data_type_other"
     purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
index 05fe159..d416063 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-photo-video.xml
@@ -1,8 +1,6 @@
 <data-labels>
-    <data-shared dataCategory="photo_video"
-        dataType="photos"
+    <data-shared dataType="photo_video_data_type_photos"
         purposes="analytics" />
-    <data-shared dataCategory="photo_video"
-        dataType="videos"
+    <data-shared dataType="photo_video_data_type_videos"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
index a5de7be..3d932d6 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-search-and-browsing.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="search_and_browsing"
-        dataType="web_browsing_history"
+    <data-shared dataType="search_and_browsing_data_type_web_browsing_history"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
index f01e2df..704cb1c 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-storage.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="storage"
-        dataType="files_docs"
+    <data-shared dataType="storage_data_type_files_docs"
         purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
index f1fbd56..bd88ada 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized-type.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="personal"
-    dataType="unrecognized"
+    <data-shared dataType="personal_data_type_unrecognized"
     purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
index c5be684..a578d73 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-category-unrecognized.xml
@@ -1,5 +1,4 @@
 <data-labels>
-    <data-shared dataCategory="unrecognized"
-    dataType="email_address"
+    <data-shared dataType="unrecognized_data_type_email_address"
     purposes="analytics" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
index 161057a..c0bd652 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-collected-shared.xml
@@ -1,11 +1,8 @@
 <data-labels>
-    <data-accessed dataCategory="location"
-        dataType="approx_location"
+    <data-accessed dataType="location_data_type_approx_location"
         purposes="app_functionality" />
-    <data-collected dataCategory="location"
-        dataType="precise_location"
+    <data-collected dataType="location_data_type_precise_location"
         purposes="app_functionality" />
-    <data-shared dataCategory="personal"
-        dataType="name"
+    <data-shared dataType="personal_data_type_name"
         purposes="app_functionality" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml
index bb45f42..d09fc3b 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-invalid-bool.xml
@@ -1,7 +1,5 @@
 <data-labels>
-    <data-accessed dataCategory="location"
-        dataType="approx_location"
-        ephemeral="false"
+    <data-accessed dataType="location_data_type_approx_location"
         isSharingOptional="false"
         purposes="app_functionality" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml
index f927bba..6e7f812 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-accessed-valid-bool.xml
@@ -1,6 +1,4 @@
 <data-labels>
-    <data-accessed dataCategory="location"
-        dataType="approx_location"
-        ephemeral="false"
+    <data-accessed dataType="location_data_type_approx_location"
         purposes="app_functionality" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml
new file mode 100644
index 0000000..ee362fe
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral-collision.xml
@@ -0,0 +1,14 @@
+<data-labels>
+    <data-collected dataType="photo_video_data_type_photos"
+        isCollectionOptional="true"
+        purposes="app_functionality" />
+    <data-collected-ephemeral dataType="location_data_type_approx_location"
+        isCollectionOptional="false"
+        purposes="app_functionality" />
+    <data-collected dataType="location_data_type_approx_location"
+        isCollectionOptional="true"
+        purposes="app_functionality" />
+    <data-collected-ephemeral dataType="contacts_data_type_contacts"
+        isCollectionOptional="true"
+        purposes="app_functionality" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml
new file mode 100644
index 0000000..79c9000
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-ephemeral.xml
@@ -0,0 +1,14 @@
+<data-labels>
+    <data-collected dataType="photo_video_data_type_photos"
+        isCollectionOptional="true"
+        purposes="app_functionality" />
+    <data-collected dataType="location_data_type_precise_location"
+        isCollectionOptional="true"
+        purposes="app_functionality" />
+    <data-collected-ephemeral dataType="location_data_type_approx_location"
+        isCollectionOptional="false"
+        purposes="app_functionality" />
+    <data-collected-ephemeral dataType="contacts_data_type_contacts"
+        isCollectionOptional="true"
+        purposes="app_functionality" />
+</data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml
index ba11afb..801fada 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-invalid-bool.xml
@@ -1,7 +1,5 @@
 <data-labels>
-    <data-collected dataCategory="location"
-        dataType="approx_location"
-        ephemeral="false"
+    <data-collected dataType="location_data_type_approx_location"
         isSharingOptional="false"
         purposes="app_functionality" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml
index 4b6d3977..1ada12d 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-collected-valid-bool.xml
@@ -1,7 +1,5 @@
 <data-labels>
-    <data-collected dataCategory="location"
-        dataType="approx_location"
-        ephemeral="false"
+    <data-collected dataType="location_data_type_approx_location"
         isCollectionOptional="false"
         purposes="app_functionality" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml
index 7840b98..b327d88 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-invalid-bool.xml
@@ -1,7 +1,5 @@
 <data-labels>
-    <data-shared dataCategory="location"
-        dataType="approx_location"
-        ephemeral="false"
+    <data-shared dataType="location_data_type_approx_location"
         isCollectionOptional="false"
         purposes="app_functionality" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml
index ccf77b0..34bd0de 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/hr/data-labels-shared-valid-bool.xml
@@ -1,7 +1,5 @@
 <data-labels>
-    <data-shared dataCategory="location"
-        dataType="approx_location"
-        ephemeral="false"
+    <data-shared dataType="location_data_type_approx_location"
         isSharingOptional="false"
         purposes="app_functionality" />
 </data-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
index 14f9ef2..974ea69 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal-partial.xml
@@ -1,17 +1,17 @@
 <pbundle_as_map name="data_labels">
     <pbundle_as_map name="data_shared">
         <pbundle_as_map name="personal">
-    <pbundle_as_map name="name">
-        <int-array name="purposes" num="2">
-            <item value="2" />
-            <item value="3" />
-        </int-array>
-    </pbundle_as_map>
-    <pbundle_as_map name="email_address">
-        <int-array name="purposes" num="1">
-            <item value="2" />
-        </int-array>
-    </pbundle_as_map>
-</pbundle_as_map>
+            <pbundle_as_map name="name">
+                <int-array name="purposes" num="2">
+                    <item value="2" />
+                    <item value="3" />
+                </int-array>
+            </pbundle_as_map>
+            <pbundle_as_map name="email_address">
+                <int-array name="purposes" num="1">
+                    <item value="2" />
+                </int-array>
+            </pbundle_as_map>
+        </pbundle_as_map>
     </pbundle_as_map>
 </pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
index 1c87de9..62c26ab 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-category-personal.xml
@@ -7,7 +7,6 @@
             <item value="3" />
         </int-array>
         <boolean name="is_sharing_optional" value="true" />
-        <boolean name="ephemeral" value="true" />
     </pbundle_as_map>
     <pbundle_as_map name="email_address">
         <int-array name="purposes" num="1">
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml
index ddefc18..df000aa 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-accessed-valid-bool.xml
@@ -5,7 +5,6 @@
                 <int-array name="purposes" num="1">
                     <item value="1"/>
                 </int-array>
-                <boolean name="ephemeral" value="false"/>
             </pbundle_as_map>
         </pbundle_as_map>
     </pbundle_as_map>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml
new file mode 100644
index 0000000..c671c4b
--- /dev/null
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-collected-ephemeral.xml
@@ -0,0 +1,38 @@
+<pbundle_as_map name="data_labels">
+    <pbundle_as_map name="data_collected">
+        <pbundle_as_map name="photo_video">
+            <pbundle_as_map name="photos">
+                <int-array name="purposes" num="1">
+                    <item value="1"/>
+                </int-array>
+                <boolean name="is_collection_optional" value="true"/>
+                <boolean name="ephemeral" value="false"/>
+            </pbundle_as_map>
+        </pbundle_as_map>
+        <pbundle_as_map name="location">
+            <pbundle_as_map name="precise_location">
+                <int-array name="purposes" num="1">
+                    <item value="1"/>
+                </int-array>
+                <boolean name="is_collection_optional" value="true"/>
+                <boolean name="ephemeral" value="false"/>
+            </pbundle_as_map>
+            <pbundle_as_map name="approx_location">
+                <int-array name="purposes" num="1">
+                    <item value="1"/>
+                </int-array>
+                <boolean name="is_collection_optional" value="false"/>
+                <boolean name="ephemeral" value="true"/>
+            </pbundle_as_map>
+        </pbundle_as_map>
+        <pbundle_as_map name="contacts">
+            <pbundle_as_map name="contacts">
+                <int-array name="purposes" num="1">
+                    <item value="1"/>
+                </int-array>
+                <boolean name="is_collection_optional" value="true"/>
+                <boolean name="ephemeral" value="true"/>
+            </pbundle_as_map>
+        </pbundle_as_map>
+    </pbundle_as_map>
+</pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
index 3864f98..0edd8fa26 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/datalabels/od/data-labels-shared-valid-bool.xml
@@ -6,7 +6,6 @@
                     <item value="1"/>
                 </int-array>
                 <boolean name="is_sharing_optional" value="false"/>
-                <boolean name="ephemeral" value="false"/>
             </pbundle_as_map>
         </pbundle_as_map>
     </pbundle_as_map>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
index 8997f4f..84456da 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/hr/with-data-labels.xml
@@ -1,9 +1,7 @@
 <safety-labels version="12345">
     <data-labels>
-        <data-shared dataCategory="location"
-            dataType="approx_location"
+        <data-shared dataType="location_data_type_approx_location"
             isSharingOptional="false"
-            ephemeral="false"
             purposes="app_functionality" />
     </data-labels>
 </safety-labels>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
index a966fda..fa2a3f8 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/safetylabels/od/with-data-labels.xml
@@ -8,7 +8,6 @@
                         <item value="1"/>
                     </int-array>
                     <boolean name="is_sharing_optional" value="false"/>
-                    <boolean name="ephemeral" value="false"/>
                 </pbundle_as_map>
             </pbundle_as_map>
         </pbundle_as_map>
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-url.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml
similarity index 100%
rename from tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-url.xml
rename to tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/missing-bool.xml
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml
index 6fe86c3..f01d7d2 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/hr/valid.xml
@@ -1 +1 @@
-<system-app-safety-label url="www.example.com"></system-app-safety-label>
\ No newline at end of file
+<system-app-safety-label declaration="true"></system-app-safety-label>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-url.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml
similarity index 100%
rename from tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-url.xml
rename to tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/missing-bool.xml
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml
index f96535b..fad631b 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/systemappsafetylabel/od/valid.xml
@@ -1,3 +1,3 @@
 <pbundle_as_map name="system_app_safety_label">
-    <string name="url" value="www.example.com"/>
+    <boolean name="declaration" value="true"/>
 </pbundle_as_map>
\ No newline at end of file
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
index 8f854ad..41b32b5 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/hr.xml
@@ -1,15 +1,13 @@
 <app-metadata-bundles version="123">
     <safety-labels version="12345">
         <data-labels>
-            <data-shared dataCategory="location"
-                dataType="approx_location"
+            <data-shared
+                dataType="location_data_type_approx_location"
                 isSharingOptional="false"
-                ephemeral="false"
                 purposes="app_functionality" />
-            <data-shared dataCategory="location"
-                dataType="precise_location"
+            <data-shared
+                dataType="location_data_type_precise_location"
                 isSharingOptional="true"
-                ephemeral="true"
                 purposes="app_functionality|analytics" />
         </data-labels>
         <security-labels
@@ -19,7 +17,7 @@
         <third-party-verification url="www.example.com">
         </third-party-verification>
     </safety-labels>
-    <system-app-safety-label url="www.example.com">
+    <system-app-safety-label declaration="true">
     </system-app-safety-label>
     <transparency-info>
         <developer-info
diff --git a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
index 8f1dc64..c11ac43 100644
--- a/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
+++ b/tools/app_metadata_bundles/src/test/resources/com/android/asllib/validmappings/general/od.xml
@@ -10,7 +10,6 @@
                             <item value="1"/>
                         </int-array>
                         <boolean name="is_sharing_optional" value="false"/>
-                        <boolean name="ephemeral" value="false"/>
                     </pbundle_as_map>
                     <pbundle_as_map name="precise_location">
                         <int-array name="purposes" num="2">
@@ -18,7 +17,6 @@
                             <item value="2"/>
                         </int-array>
                         <boolean name="is_sharing_optional" value="true"/>
-                        <boolean name="ephemeral" value="true"/>
                     </pbundle_as_map>
                 </pbundle_as_map>
             </pbundle_as_map>
@@ -32,7 +30,7 @@
         </pbundle_as_map>
     </pbundle_as_map>
     <pbundle_as_map name="system_app_safety_label">
-        <string name="url" value="www.example.com"/>
+        <boolean name="declaration" value="true"/>
     </pbundle_as_map>
     <pbundle_as_map name="transparency_info">
         <pbundle_as_map name="developer_info">
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
old mode 100755
new mode 100644
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index 81ac897..8f67fa8 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -22,6 +22,8 @@
 import os.path
 import sys
 
+import xml.etree.ElementTree as ElementTree
+
 
 def get_locale_parts(locale):
     """Split a locale into three parts, for langauge, script, and region."""
@@ -40,42 +42,43 @@
 
 def read_likely_subtags(input_file_name):
     """Read and parse ICU's likelySubtags.txt."""
-    with open(input_file_name) as input_file:
-        likely_script_dict = {
-            # Android's additions for pseudo-locales. These internal codes make
-            # sure that the pseudo-locales would not match other English or
-            # Arabic locales. (We can't use private-use ISO 15924 codes, since
-            # they may be used by apps for other purposes.)
-            "en_XA": "~~~A",
-            "ar_XB": "~~~B",
-            # Removed data from later versions of ICU
-            "ji": "Hebr", # Old code for Yiddish, still used in Java and Android
-        }
-        representative_locales = {
-            # Android's additions
-            "en_Latn_GB", # representative for en_Latn_001
-            "es_Latn_MX", # representative for es_Latn_419
-            "es_Latn_US", # representative for es_Latn_419 (not the best idea,
-                          # but Android has been shipping with it for quite a
-                          # while. Fortunately, MX < US, so if both exist, MX
-                          # would be chosen.)
-        }
-        for line in input_file:
-            line = line.strip(u' \n\uFEFF')
-            if line.startswith('//'):
-                continue
-            if '{' in line and '}' in line:
-                from_locale = line[:line.index('{')]
-                to_locale = line[line.index('"')+1:line.rindex('"')]
-                from_lang, from_scr, from_region = get_locale_parts(from_locale)
-                _, to_scr, to_region = get_locale_parts(to_locale)
-                if from_lang == 'und':
-                    continue  # not very useful for our purposes
-                if from_region is None and to_region not in ['001', 'ZZ']:
-                    representative_locales.add(to_locale)
-                if from_scr is None:
-                    likely_script_dict[from_locale] = to_scr
-        return likely_script_dict, frozenset(representative_locales)
+    likely_script_dict = {
+        # Android's additions for pseudo-locales. These internal codes make
+        # sure that the pseudo-locales would not match other English or
+        # Arabic locales. (We can't use private-use ISO 15924 codes, since
+        # they may be used by apps for other purposes.)
+        "en_XA": "~~~A",
+        "ar_XB": "~~~B",
+        # Removed data from later versions of ICU
+        "ji": "Hebr", # Old code for Yiddish, still used in Java and Android
+    }
+    representative_locales = {
+        # Android's additions
+        "en_Latn_GB", # representative for en_Latn_001
+        "es_Latn_MX", # representative for es_Latn_419
+        "es_Latn_US", # representative for es_Latn_419 (not the best idea,
+        # but Android has been shipping with it for quite a
+        # while. Fortunately, MX < US, so if both exist, MX
+        # would be chosen.)
+    }
+    xml_tree = ElementTree.parse(input_file_name)
+    likely_subtags = xml_tree.find('likelySubtags')
+    for child in likely_subtags:
+        from_locale = child.get('from')
+        to_locale = child.get('to')
+        # print(f'from: {from_locale} to: {to_locale}')
+        from_lang, from_scr, from_region = get_locale_parts(from_locale)
+        _, to_scr, to_region = get_locale_parts(to_locale)
+        if to_locale == "FAIL":
+            continue # "FAIL" cases are not useful here.
+        if from_lang == 'und':
+            continue  # not very useful for our purposes
+        if from_region is None and to_region not in ['001', 'ZZ']:
+            representative_locales.add(to_locale)
+        if from_scr is None:
+            likely_script_dict[from_locale] = to_scr
+
+    return likely_script_dict, frozenset(representative_locales)
 
 
 # From packLanguageOrRegion() in ResourceTypes.cpp
@@ -86,7 +89,7 @@
     elif len(inp) == 2:
         return ord(inp[0]), ord(inp[1])
     else:
-        assert len(inp) == 3
+        assert len(inp) == 3, f'Expects a 3-character string, but "{inp}" '
         base = ord(base)
         first = ord(inp[0]) - base
         second = ord(inp[1]) - base
@@ -161,9 +164,10 @@
     print('});')
 
 
-def read_and_dump_likely_data(icu_data_dir):
+def read_and_dump_likely_data(cldr_source_dir):
     """Read and dump the likely-script data."""
-    likely_subtags_txt = os.path.join(icu_data_dir, 'misc', 'likelySubtags.txt')
+    likely_subtags_txt = os.path.join(cldr_source_dir,
+                                      'common', 'supplemental', 'likelySubtags.xml')
     likely_script_dict, representative_locales = read_likely_subtags(
         likely_subtags_txt)
 
@@ -280,10 +284,11 @@
     icu_data_dir = os.path.join(
         source_root,
         'external', 'icu', 'icu4c', 'source', 'data')
+    cldr_source_dir = os.path.join(source_root, 'external', 'cldr')
 
     print('// Auto-generated by %s' % sys.argv[0])
     print()
-    likely_script_dict = read_and_dump_likely_data(icu_data_dir)
+    likely_script_dict = read_and_dump_likely_data(cldr_source_dir)
     read_and_dump_parent_data(icu_data_dir, likely_script_dict)
 
 
diff --git a/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java b/wifi/java/src/android/net/wifi/SoftApConfToXmlMigrationUtil.java
old mode 100755
new mode 100644
diff --git a/wifi/java/src/android/net/wifi/WifiMigration.java b/wifi/java/src/android/net/wifi/WifiMigration.java
old mode 100755
new mode 100644
diff --git a/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java
old mode 100755
new mode 100644